Add support to Drag and Drop to FileHandlers #116047
|
@ -760,8 +760,8 @@ endif()
|
|||
|
||||
# Unit testing
|
||||
option(WITH_GTESTS "Enable GTest unit testing" OFF)
|
||||
option(WITH_OPENGL_RENDER_TESTS "Enable OpenGL render related unit testing (Experimental)" OFF)
|
||||
option(WITH_OPENGL_DRAW_TESTS "Enable OpenGL UI drawing related unit testing (Experimental)" OFF)
|
||||
option(WITH_GPU_RENDER_TESTS "Enable GPU render related unit testing (EEVEE, Workbench and Grease Pencil)" OFF)
|
||||
option(WITH_GPU_DRAW_TESTS "Enable GPU drawing related unit testing (GPU backends and draw manager)" OFF)
|
||||
option(WITH_COMPOSITOR_REALTIME_TESTS "Enable regression testing for realtime compositor" OFF)
|
||||
if(UNIX AND NOT (APPLE OR HAIKU))
|
||||
option(WITH_UI_TESTS "\
|
||||
|
|
|
@ -17,6 +17,7 @@ ExternalProject_Add(external_ocloc
|
|||
PREFIX ${BUILD_DIR}/ocloc
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/ocloc ${DEFAULT_CMAKE_FLAGS} ${OCLOC_EXTRA_ARGS}
|
||||
INSTALL_DIR ${LIBDIR}/ocloc
|
||||
PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/ocloc/src/external_ocloc/ < ${PATCH_DIR}/ocloc.diff
|
||||
)
|
||||
|
||||
add_dependencies(
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
diff --git a/shared/offline_compiler/source/ocloc_fatbinary.cpp b/shared/offline_compiler/source/ocloc_fatbinary.cpp
|
||||
index 98a1c0e..4d9b5b0 100644
|
||||
--- a/shared/offline_compiler/source/ocloc_fatbinary.cpp
|
||||
+++ b/shared/offline_compiler/source/ocloc_fatbinary.cpp
|
||||
@@ -286,7 +286,9 @@ int buildFatBinaryForTarget(int retVal, const std::vector<std::string> &argsCopy
|
||||
productConfig = ProductConfigHelper::parseMajorMinorRevisionValue(argHelper->productConfigHelper->getProductConfigFromDeviceName(product));
|
||||
}
|
||||
|
||||
- fatbinary.appendFileEntry(pointerSize + "." + productConfig, pCompiler->getPackedDeviceBinaryOutput());
|
||||
+ // Storing binaries under the hardware prefix instead of the full architecture version number,
|
||||
+ // as they would otherwise be ignored if they do not fully match that of the execution device.
|
||||
+ fatbinary.appendFileEntry(pointerSize + "." + NEO::hardwarePrefix[argHelper->productConfigHelper->getProductFamilyFromDeviceName(productConfig)], pCompiler->getPackedDeviceBinaryOutput());
|
||||
return retVal;
|
||||
}
|
|
@ -17,8 +17,8 @@ if NOT "%1" == "" (
|
|||
shift /1
|
||||
) else if "%1" == "with_tests" (
|
||||
set TESTS_CMAKE_ARGS=%TESTS_CMAKE_ARGS% -DWITH_GTESTS=On
|
||||
) else if "%1" == "with_opengl_tests" (
|
||||
set TESTS_CMAKE_ARGS=%TESTS_CMAKE_ARGS% -DWITH_OPENGL_DRAW_TESTS=On -DWITH_OPENGL_RENDER_TESTS=On
|
||||
) else if "%1" == "with_gpu_tests" (
|
||||
set TESTS_CMAKE_ARGS=%TESTS_CMAKE_ARGS% -DWITH_GPU_DRAW_TESTS=On -DWITH_GPU_RENDER_TESTS=On
|
||||
) else if "%1" == "full" (
|
||||
set TARGET=Full
|
||||
set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% ^
|
||||
|
|
|
@ -37,7 +37,7 @@ echo - doc_py ^(Generate sphinx python api docs^)
|
|||
|
||||
echo.
|
||||
echo Experimental options
|
||||
echo - with_opengl_tests ^(enable both the render and draw opengl test suites^)
|
||||
echo - with_gpu_tests ^(enable both the render and draw gpu test suites including EEVEE, Workbench, Grease Pencil, draw manager and GPU backends^)
|
||||
echo - clang ^(enable building with clang^)
|
||||
echo - asan ^(enable asan when building with clang^)
|
||||
echo - ninja ^(enable building with ninja instead of msbuild^)
|
||||
|
|
|
@ -7,4 +7,5 @@ GPL-2.0-or-later GPL-license.txt https://spdx.org/licenses/GP
|
|||
GPL-3.0-or-later GPL3-license.txt https://spdx.org/licenses/GPL-3.0-or-later.html
|
||||
LGPL-2.1-or-later LGPL2.1-license.txt https://spdx.org/licenses/LGPL-2.1-or-later.html
|
||||
MIT MIT-license.txt https://spdx.org/licenses/MIT.html
|
||||
MPL-2.0 MPL-2.0.txt https://spdx.org/licenses/MPL-2.0.html
|
||||
Zlib Zlib-license.txt https://spdx.org/licenses/Zlib.html
|
||||
|
|
|
@ -60,7 +60,7 @@ class CPUKernels {
|
|||
int x,
|
||||
int y,
|
||||
float threshold,
|
||||
bool reset,
|
||||
int reset,
|
||||
int offset,
|
||||
int stride)>;
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class OneapiDeviceQueue : public DeviceQueue {
|
|||
virtual void copy_to_device(device_memory &mem) override;
|
||||
virtual void copy_from_device(device_memory &mem) override;
|
||||
|
||||
virtual bool supports_local_atomic_sort() const
|
||||
virtual bool supports_local_atomic_sort() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ struct DeviceKernelArguments {
|
|||
POINTER,
|
||||
INT32,
|
||||
FLOAT32,
|
||||
BOOLEAN,
|
||||
KERNEL_FILM_CONVERT,
|
||||
};
|
||||
|
||||
|
@ -66,10 +65,6 @@ struct DeviceKernelArguments {
|
|||
{
|
||||
add(FLOAT32, value, sizeof(float));
|
||||
}
|
||||
void add(const bool *value)
|
||||
{
|
||||
add(BOOLEAN, value, 4);
|
||||
}
|
||||
void add(const Type type, const void *value, size_t size)
|
||||
{
|
||||
assert(count < MAX_ARGS);
|
||||
|
|
|
@ -103,7 +103,7 @@ class DenoiserGPU : public Denoiser {
|
|||
int denoised_offset;
|
||||
|
||||
int num_components;
|
||||
bool use_compositing;
|
||||
int use_compositing;
|
||||
bool use_denoising_albedo;
|
||||
};
|
||||
|
||||
|
|
|
@ -1055,6 +1055,7 @@ int PathTraceWorkGPU::adaptive_sampling_convergence_check_count_active(float thr
|
|||
queue_->zero_to_device(num_active_pixels);
|
||||
|
||||
const int work_size = effective_buffer_params_.width * effective_buffer_params_.height;
|
||||
const int reset_int = reset; /* No bool kernel arguments. */
|
||||
|
||||
DeviceKernelArguments args(&buffers_->buffer.device_pointer,
|
||||
&effective_buffer_params_.full_x,
|
||||
|
@ -1062,7 +1063,7 @@ int PathTraceWorkGPU::adaptive_sampling_convergence_check_count_active(float thr
|
|||
&effective_buffer_params_.width,
|
||||
&effective_buffer_params_.height,
|
||||
&threshold,
|
||||
&reset,
|
||||
&reset_int,
|
||||
&effective_buffer_params_.offset,
|
||||
&effective_buffer_params_.stride,
|
||||
&num_active_pixels.device_pointer);
|
||||
|
|
|
@ -101,7 +101,7 @@ bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)(
|
|||
int x,
|
||||
int y,
|
||||
float threshold,
|
||||
bool reset,
|
||||
int reset,
|
||||
int offset,
|
||||
int stride);
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)(
|
|||
int x,
|
||||
int y,
|
||||
float threshold,
|
||||
bool reset,
|
||||
int reset,
|
||||
int offset,
|
||||
int stride)
|
||||
{
|
||||
|
|
|
@ -668,7 +668,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
|||
int sw,
|
||||
int sh,
|
||||
float threshold,
|
||||
bool reset,
|
||||
int reset,
|
||||
int offset,
|
||||
int stride,
|
||||
ccl_global uint *num_active_pixels)
|
||||
|
@ -1104,7 +1104,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
|||
int pass_denoised,
|
||||
int pass_sample_count,
|
||||
int num_components,
|
||||
bool use_compositing)
|
||||
int use_compositing)
|
||||
{
|
||||
const int work_index = ccl_gpu_global_id_x();
|
||||
const int y = work_index / width;
|
||||
|
|
|
@ -34,7 +34,7 @@ ccl_device bool film_adaptive_sampling_convergence_check(KernelGlobals kg,
|
|||
int x,
|
||||
int y,
|
||||
float threshold,
|
||||
bool reset,
|
||||
int reset,
|
||||
int offset,
|
||||
int stride)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright Contributors to the OpenVDB Project
|
||||
/* SPDX-FileCopyrightText: 2020-2021 Contributors to the OpenVDB Project
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This is an extract from NanoVDB.h, with minimal code needed for kernel side access to grids. The
|
||||
|
|
|
@ -3372,14 +3372,23 @@ PrincipledHairBsdfNode::PrincipledHairBsdfNode() : BsdfBaseNode(get_node_type())
|
|||
closure = CLOSURE_BSDF_HAIR_HUANG_ID;
|
||||
}
|
||||
|
||||
void PrincipledHairBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
|
||||
/* Treat hair as transparent if the hit is outside of the projected width. */
|
||||
bool PrincipledHairBsdfNode::has_surface_transparent()
|
||||
{
|
||||
if (model == NODE_PRINCIPLED_HAIR_HUANG) {
|
||||
/* Make sure we have the normal for elliptical cross section tracking. */
|
||||
if (aspect_ratio != 1.0f || input("Aspect Ratio")->link) {
|
||||
attributes->add(ATTR_STD_VERTEX_NORMAL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrincipledHairBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
|
||||
{
|
||||
if (has_surface_transparent()) {
|
||||
/* Make sure we have the normal for elliptical cross section tracking. */
|
||||
attributes->add(ATTR_STD_VERTEX_NORMAL);
|
||||
}
|
||||
|
||||
if (!input("Random")->link) {
|
||||
/* Enable retrieving Hair Info -> Random if Random isn't linked. */
|
||||
|
|
|
@ -851,6 +851,8 @@ class PrincipledHairBsdfNode : public BsdfBaseNode {
|
|||
{
|
||||
return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_PRINCIPLED_HAIR;
|
||||
}
|
||||
|
||||
bool has_surface_transparent();
|
||||
};
|
||||
|
||||
class HairBsdfNode : public BsdfNode {
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
|
@ -6075,8 +6075,7 @@ def km_edit_curves(params):
|
|||
|
||||
("curves.set_selection_domain", {"type": 'ONE', "value": 'PRESS'}, {"properties": [("domain", 'POINT')]}),
|
||||
("curves.set_selection_domain", {"type": 'TWO', "value": 'PRESS'}, {"properties": [("domain", 'CURVE')]}),
|
||||
("curves.disable_selection", {"type": 'ONE', "value": 'PRESS', "alt": True}, None),
|
||||
("curves.disable_selection", {"type": 'TWO', "value": 'PRESS', "alt": True}, None),
|
||||
("curves.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None),
|
||||
*_template_items_select_actions(params, "curves.select_all"),
|
||||
("curves.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
|
||||
("curves.delete", {"type": 'X', "value": 'PRESS'}, None),
|
||||
|
@ -7725,6 +7724,18 @@ def km_3d_view_tool_edit_curve_draw(params):
|
|||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_edit_curves_draw(params):
|
||||
return (
|
||||
"3D View Tool: Edit Curves, Draw",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
# No need for `tool_modifier` since this takes all input.
|
||||
("curves.draw", {"type": params.tool_mouse, "value": 'PRESS'},
|
||||
{"properties": [("wait_for_input", False)]}),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_edit_curve_pen(params):
|
||||
return (
|
||||
"3D View Tool: Edit Curve, Curve Pen",
|
||||
|
@ -8666,6 +8677,7 @@ def generate_keymaps(params=None):
|
|||
km_3d_view_tool_edit_curve_randomize(params),
|
||||
km_3d_view_tool_edit_curve_extrude(params),
|
||||
km_3d_view_tool_edit_curve_extrude_to_cursor(params),
|
||||
km_3d_view_tool_edit_curves_draw(params),
|
||||
km_3d_view_tool_sculpt_box_hide(params),
|
||||
km_3d_view_tool_sculpt_box_mask(params),
|
||||
km_3d_view_tool_sculpt_lasso_mask(params),
|
||||
|
|
|
@ -4117,8 +4117,7 @@ def km_curves(params):
|
|||
# Selection Modes
|
||||
("curves.set_selection_domain", {"type": 'ONE', "value": 'PRESS'}, {"properties": [("domain", 'POINT')]}),
|
||||
("curves.set_selection_domain", {"type": 'TWO', "value": 'PRESS'}, {"properties": [("domain", 'CURVE')]}),
|
||||
("curves.disable_selection", {"type": 'ONE', "value": 'PRESS', "alt": True}, None),
|
||||
("curves.disable_selection", {"type": 'TWO', "value": 'PRESS', "alt": True}, None),
|
||||
("curves.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
|
||||
# Selection Operators
|
||||
("curves.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
|
||||
("curves.select_all", {"type": 'A', "value": 'PRESS', "shift": True,
|
||||
|
|
|
@ -283,6 +283,7 @@ class NODE_MT_geometry_node_GEO_INPUT_SCENE(Menu):
|
|||
layout = self.layout
|
||||
if context.space_data.geometry_nodes_type == 'TOOL':
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeTool3DCursor")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeInputActiveCamera")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeCollectionInfo")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeImageInfo")
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeIsViewport")
|
||||
|
|
|
@ -1148,65 +1148,66 @@ class _defs_edit_mesh:
|
|||
)
|
||||
|
||||
|
||||
def curve_draw_settings(context, layout, _tool, *, extra=False):
|
||||
# Tool settings initialize operator options.
|
||||
tool_settings = context.tool_settings
|
||||
cps = tool_settings.curve_paint_settings
|
||||
region_type = context.region.type
|
||||
|
||||
if region_type == 'TOOL_HEADER':
|
||||
if not extra:
|
||||
layout.prop(cps, "curve_type", text="")
|
||||
layout.prop(cps, "depth_mode", expand=True)
|
||||
layout.popover("TOPBAR_PT_tool_settings_extra", text="...")
|
||||
return
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
if region_type != 'TOOL_HEADER':
|
||||
layout.prop(cps, "curve_type")
|
||||
layout.separator()
|
||||
if cps.curve_type == 'BEZIER':
|
||||
layout.prop(cps, "fit_method")
|
||||
layout.prop(cps, "error_threshold")
|
||||
if region_type != 'TOOL_HEADER':
|
||||
row = layout.row(heading="Detect Corners", align=True)
|
||||
else:
|
||||
row = layout.row(heading="Corners", align=True)
|
||||
row.prop(cps, "use_corners_detect", text="")
|
||||
sub = row.row(align=True)
|
||||
sub.active = cps.use_corners_detect
|
||||
sub.prop(cps, "corner_angle", text="")
|
||||
layout.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(cps, "radius_taper_start", text="Taper Start", slider=True)
|
||||
col.prop(cps, "radius_taper_end", text="End", slider=True)
|
||||
col = layout.column(align=True)
|
||||
col.prop(cps, "radius_min", text="Radius Min")
|
||||
col.prop(cps, "radius_max", text="Max")
|
||||
col.prop(cps, "use_pressure_radius")
|
||||
|
||||
if region_type != 'TOOL_HEADER' or cps.depth_mode == 'SURFACE':
|
||||
layout.separator()
|
||||
|
||||
if region_type != 'TOOL_HEADER':
|
||||
row = layout.row()
|
||||
row.prop(cps, "depth_mode", expand=True)
|
||||
if cps.depth_mode == 'SURFACE':
|
||||
col = layout.column()
|
||||
col.prop(cps, "surface_offset")
|
||||
col.prop(cps, "use_offset_absolute")
|
||||
col.prop(cps, "use_stroke_endpoints")
|
||||
if cps.use_stroke_endpoints:
|
||||
colsub = layout.column(align=True)
|
||||
colsub.prop(cps, "surface_plane")
|
||||
|
||||
|
||||
class _defs_edit_curve:
|
||||
|
||||
@ToolDef.from_fn
|
||||
def draw():
|
||||
def draw_settings(context, layout, _tool, *, extra=False):
|
||||
# Tool settings initialize operator options.
|
||||
tool_settings = context.tool_settings
|
||||
cps = tool_settings.curve_paint_settings
|
||||
region_type = context.region.type
|
||||
|
||||
if region_type == 'TOOL_HEADER':
|
||||
if not extra:
|
||||
layout.prop(cps, "curve_type", text="")
|
||||
layout.prop(cps, "depth_mode", expand=True)
|
||||
layout.popover("TOPBAR_PT_tool_settings_extra", text="...")
|
||||
return
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
if region_type != 'TOOL_HEADER':
|
||||
layout.prop(cps, "curve_type")
|
||||
layout.separator()
|
||||
if cps.curve_type == 'BEZIER':
|
||||
layout.prop(cps, "fit_method")
|
||||
layout.prop(cps, "error_threshold")
|
||||
if region_type != 'TOOL_HEADER':
|
||||
row = layout.row(heading="Detect Corners", align=True)
|
||||
else:
|
||||
row = layout.row(heading="Corners", align=True)
|
||||
row.prop(cps, "use_corners_detect", text="")
|
||||
sub = row.row(align=True)
|
||||
sub.active = cps.use_corners_detect
|
||||
sub.prop(cps, "corner_angle", text="")
|
||||
layout.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(cps, "radius_taper_start", text="Taper Start", slider=True)
|
||||
col.prop(cps, "radius_taper_end", text="End", slider=True)
|
||||
col = layout.column(align=True)
|
||||
col.prop(cps, "radius_min", text="Radius Min")
|
||||
col.prop(cps, "radius_max", text="Max")
|
||||
col.prop(cps, "use_pressure_radius")
|
||||
|
||||
if region_type != 'TOOL_HEADER' or cps.depth_mode == 'SURFACE':
|
||||
layout.separator()
|
||||
|
||||
if region_type != 'TOOL_HEADER':
|
||||
row = layout.row()
|
||||
row.prop(cps, "depth_mode", expand=True)
|
||||
if cps.depth_mode == 'SURFACE':
|
||||
col = layout.column()
|
||||
col.prop(cps, "surface_offset")
|
||||
col.prop(cps, "use_offset_absolute")
|
||||
col.prop(cps, "use_stroke_endpoints")
|
||||
if cps.use_stroke_endpoints:
|
||||
colsub = layout.column(align=True)
|
||||
colsub.prop(cps, "surface_plane")
|
||||
|
||||
return dict(
|
||||
idname="builtin.draw",
|
||||
label="Draw",
|
||||
|
@ -1214,7 +1215,7 @@ class _defs_edit_curve:
|
|||
icon="ops.curve.draw",
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
draw_settings=curve_draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
|
@ -1295,6 +1296,29 @@ class _defs_edit_curve:
|
|||
)
|
||||
|
||||
|
||||
class _defs_edit_curves:
|
||||
|
||||
@ToolDef.from_fn
|
||||
def draw():
|
||||
def curve_draw(context, layout, tool, *, extra=False):
|
||||
curve_draw_settings(context, layout, tool, extra=extra)
|
||||
|
||||
if extra:
|
||||
props = tool.operator_properties("curves.draw")
|
||||
col = layout.column(align=True)
|
||||
col.prop(props, "is_curve_2d", text="Curve 2D")
|
||||
|
||||
return dict(
|
||||
idname="builtin.draw",
|
||||
label="Draw",
|
||||
cursor='PAINT_BRUSH',
|
||||
icon="ops.curve.draw",
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=curve_draw,
|
||||
)
|
||||
|
||||
|
||||
class _defs_edit_text:
|
||||
|
||||
@ToolDef.from_fn
|
||||
|
@ -3003,6 +3027,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
*_tools_default,
|
||||
None,
|
||||
_defs_edit_curve.curve_radius,
|
||||
_defs_edit_curves.draw,
|
||||
],
|
||||
'EDIT_SURFACE': [
|
||||
*_tools_default,
|
||||
|
|
|
@ -5887,6 +5887,8 @@ class VIEW3D_MT_edit_curves(Menu):
|
|||
|
||||
layout.menu("VIEW3D_MT_transform")
|
||||
layout.separator()
|
||||
layout.operator("curves.duplicate_move")
|
||||
layout.separator()
|
||||
layout.operator("curves.attribute_set")
|
||||
layout.operator("curves.delete")
|
||||
layout.template_node_operator_asset_menu_items(catalog_path=self.bl_label)
|
||||
|
|
|
@ -32,6 +32,9 @@ namespace blender::animrig {
|
|||
/** \name Key-Framing Management
|
||||
* \{ */
|
||||
|
||||
/* Set the FCurve flag based on the property type of `prop`. */
|
||||
void update_autoflags_fcurve_direct(FCurve *fcu, PropertyRNA *prop);
|
||||
|
||||
/**
|
||||
* \brief Main Insert Key-framing API call.
|
||||
*
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "BKE_fcurve.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "DNA_anim_types.h"
|
||||
#include "ED_anim_api.hh"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender::animrig {
|
||||
|
@ -240,9 +239,8 @@ void initialize_bezt(BezTriple *beztr,
|
|||
beztr->ipo = BEZT_IPO_LIN;
|
||||
}
|
||||
|
||||
/* Set keyframe type value (supplied), which should come from the scene
|
||||
* settings in most cases. */
|
||||
BEZKEYTYPE(beztr) = settings.keyframe_type;
|
||||
/* The keytype is hackily stored on the hide flag since that isn't used in animation. */
|
||||
beztr->hide = settings.keyframe_type;
|
||||
|
||||
/* Set default values for "easing" interpolation mode settings.
|
||||
* NOTE: Even if these modes aren't currently used, if users switch
|
||||
|
|
|
@ -45,6 +45,28 @@
|
|||
|
||||
namespace blender::animrig {
|
||||
|
||||
void update_autoflags_fcurve_direct(FCurve *fcu, PropertyRNA *prop)
|
||||
{
|
||||
/* Set additional flags for the F-Curve (i.e. only integer values). */
|
||||
fcu->flag &= ~(FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES);
|
||||
switch (RNA_property_type(prop)) {
|
||||
case PROP_FLOAT:
|
||||
/* Do nothing. */
|
||||
break;
|
||||
case PROP_INT:
|
||||
/* Do integer (only 'whole' numbers) interpolation between all points. */
|
||||
fcu->flag |= FCURVE_INT_VALUES;
|
||||
break;
|
||||
default:
|
||||
/* Do 'discrete' (i.e. enum, boolean values which cannot take any intermediate
|
||||
* values at all) interpolation between all points.
|
||||
* - however, we must also ensure that evaluated values are only integers still.
|
||||
*/
|
||||
fcu->flag |= (FCURVE_DISCRETE_VALUES | FCURVE_INT_VALUES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to make curves newly added to a cyclic Action cycle with the correct period. */
|
||||
static void make_new_fcurve_cyclic(const bAction *act, FCurve *fcu)
|
||||
{
|
||||
|
|
|
@ -656,7 +656,7 @@ inline TreeNode &Layer::as_node()
|
|||
TREENODE_COMMON_METHODS_FORWARD_IMPL(Layer);
|
||||
inline bool Layer::is_empty() const
|
||||
{
|
||||
return (this->frames().size() == 0);
|
||||
return (this->frames().is_empty());
|
||||
}
|
||||
inline LayerGroup &Layer::parent_group() const
|
||||
{
|
||||
|
|
|
@ -1325,6 +1325,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
|
|||
#define GEO_NODE_SPLIT_TO_INSTANCES 2116
|
||||
#define GEO_NODE_INPUT_NAMED_LAYER_SELECTION 2117
|
||||
#define GEO_NODE_INDEX_SWITCH 2118
|
||||
#define GEO_NODE_INPUT_ACTIVE_CAMERA 2119
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -376,14 +376,10 @@ void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex);
|
|||
|
||||
blender::Span<int> BKE_pbvh_node_get_grid_indices(const PBVHNode &node);
|
||||
|
||||
void BKE_pbvh_node_num_verts(const PBVH *pbvh,
|
||||
const PBVHNode *node,
|
||||
int *r_uniquevert,
|
||||
int *r_totvert);
|
||||
int BKE_pbvh_node_num_unique_verts(const PBVH &pbvh, const PBVHNode &node);
|
||||
blender::Span<int> BKE_pbvh_node_get_vert_indices(const PBVHNode *node);
|
||||
blender::Span<int> BKE_pbvh_node_get_unique_vert_indices(const PBVHNode *node);
|
||||
void BKE_pbvh_node_get_loops(const PBVHNode *node, const int **r_loop_indices);
|
||||
blender::Span<int> BKE_pbvh_node_get_loops(const PBVHNode *node);
|
||||
|
||||
void BKE_pbvh_node_calc_face_indices(const PBVH &pbvh,
|
||||
const PBVHNode &node,
|
||||
|
@ -633,9 +629,6 @@ void BKE_pbvh_store_colors_vertex(PBVH *pbvh,
|
|||
|
||||
bool BKE_pbvh_is_drawing(const PBVH *pbvh);
|
||||
|
||||
/* Do not call in PBVH_GRIDS mode */
|
||||
void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop);
|
||||
|
||||
void BKE_pbvh_update_active_vcol(PBVH *pbvh, Mesh *mesh);
|
||||
|
||||
void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color[4]);
|
||||
|
|
|
@ -200,7 +200,7 @@ struct NodeData {
|
|||
{
|
||||
undo_regions.clear();
|
||||
for (UDIMTilePixels &tile : tiles) {
|
||||
if (tile.pixel_rows.size() == 0) {
|
||||
if (tile.pixel_rows.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ FileHandlerType *BKE_file_handler_find(const char *name)
|
|||
|
||||
void BKE_file_handler_add(std::unique_ptr<FileHandlerType> file_handler)
|
||||
{
|
||||
BLI_assert(BKE_file_handler_find(file_handler->idname) != nullptr);
|
||||
BLI_assert(BKE_file_handler_find(file_handler->idname) == nullptr);
|
||||
|
||||
/** Load all extensions from the string list into the list. */
|
||||
const char char_separator = ';';
|
||||
|
|
|
@ -776,7 +776,7 @@ static GVArray adapt_mesh_attribute_domain(const Mesh &mesh,
|
|||
if (!varray) {
|
||||
return {};
|
||||
}
|
||||
if (varray.size() == 0) {
|
||||
if (varray.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
if (from_domain == to_domain) {
|
||||
|
|
|
@ -771,7 +771,7 @@ FramesMapKey Layer::frame_key_at(const int frame_number) const
|
|||
{
|
||||
Span<int> sorted_keys = this->sorted_keys();
|
||||
/* No keyframes, return no drawing. */
|
||||
if (sorted_keys.size() == 0) {
|
||||
if (sorted_keys.is_empty()) {
|
||||
return -1;
|
||||
}
|
||||
/* Before the first drawing, return no drawing. */
|
||||
|
@ -1661,7 +1661,7 @@ static void remove_drawings_unchecked(GreasePencil &grease_pencil,
|
|||
Span<int64_t> sorted_indices_to_remove)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
if (grease_pencil.drawing_array_num == 0 || sorted_indices_to_remove.size() == 0) {
|
||||
if (grease_pencil.drawing_array_num == 0 || sorted_indices_to_remove.is_empty()) {
|
||||
return;
|
||||
}
|
||||
const int64_t drawings_to_remove = sorted_indices_to_remove.size();
|
||||
|
|
|
@ -112,7 +112,7 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
|
|||
|
||||
/* Write point attributes. */
|
||||
IndexRange stroke_points_range = points_by_curve[stroke_i];
|
||||
if (stroke_points_range.size() == 0) {
|
||||
if (stroke_points_range.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -817,7 +817,7 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
|
|||
{
|
||||
#ifdef WITH_GMP
|
||||
BLI_assert(transforms.is_empty() || meshes.size() == transforms.size());
|
||||
BLI_assert(material_remaps.size() == 0 || material_remaps.size() == meshes.size());
|
||||
BLI_assert(material_remaps.is_empty() || material_remaps.size() == meshes.size());
|
||||
if (meshes.size() <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -506,7 +506,7 @@ static void face_edge_loop_islands_calc(const int totedge,
|
|||
int tot_group = 0;
|
||||
bool group_id_overflow = false;
|
||||
|
||||
if (faces.size() == 0) {
|
||||
if (faces.is_empty()) {
|
||||
*r_totgroup = 0;
|
||||
*r_face_groups = nullptr;
|
||||
if (r_edge_borders) {
|
||||
|
|
|
@ -318,7 +318,7 @@ void looptris_calc_with_normals(const Span<float3> vert_positions,
|
|||
const Span<float3> face_normals,
|
||||
MutableSpan<MLoopTri> looptris)
|
||||
{
|
||||
BLI_assert(!face_normals.is_empty() || faces.size() == 0);
|
||||
BLI_assert(!face_normals.is_empty() || faces.is_empty());
|
||||
looptris_calc_all(vert_positions, faces, corner_verts, face_normals, looptris);
|
||||
}
|
||||
|
||||
|
|
|
@ -1780,11 +1780,9 @@ void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex)
|
|||
pbvh->vert_bitmap[vertex.i] = true;
|
||||
}
|
||||
|
||||
void BKE_pbvh_node_get_loops(const PBVHNode *node, const int **r_loop_indices)
|
||||
blender::Span<int> BKE_pbvh_node_get_loops(const PBVHNode *node)
|
||||
{
|
||||
if (r_loop_indices) {
|
||||
*r_loop_indices = node->loop_indices.data();
|
||||
}
|
||||
return node->loop_indices;
|
||||
}
|
||||
|
||||
int BKE_pbvh_num_faces(const PBVH *pbvh)
|
||||
|
@ -1851,43 +1849,6 @@ blender::Vector<int> BKE_pbvh_node_calc_face_indices(const PBVH &pbvh, const PBV
|
|||
return faces;
|
||||
}
|
||||
|
||||
void BKE_pbvh_node_num_verts(const PBVH *pbvh,
|
||||
const PBVHNode *node,
|
||||
int *r_uniquevert,
|
||||
int *r_totvert)
|
||||
{
|
||||
int tot;
|
||||
|
||||
switch (pbvh->header.type) {
|
||||
case PBVH_GRIDS:
|
||||
tot = node->prim_indices.size() * pbvh->gridkey.grid_area;
|
||||
if (r_totvert) {
|
||||
*r_totvert = tot;
|
||||
}
|
||||
if (r_uniquevert) {
|
||||
*r_uniquevert = tot;
|
||||
}
|
||||
break;
|
||||
case PBVH_FACES:
|
||||
if (r_totvert) {
|
||||
*r_totvert = node->uniq_verts + node->face_verts;
|
||||
}
|
||||
if (r_uniquevert) {
|
||||
*r_uniquevert = node->uniq_verts;
|
||||
}
|
||||
break;
|
||||
case PBVH_BMESH:
|
||||
tot = node->bm_unique_verts.size();
|
||||
if (r_totvert) {
|
||||
*r_totvert = tot + node->bm_other_verts.size();
|
||||
}
|
||||
if (r_uniquevert) {
|
||||
*r_uniquevert = tot;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int BKE_pbvh_node_num_unique_verts(const PBVH &pbvh, const PBVHNode &node)
|
||||
{
|
||||
switch (pbvh.header.type) {
|
||||
|
@ -2985,8 +2946,22 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
|
|||
vi->vert_positions = {};
|
||||
vi->vertex.i = 0LL;
|
||||
|
||||
int uniq_verts, totvert;
|
||||
BKE_pbvh_node_num_verts(pbvh, node, &uniq_verts, &totvert);
|
||||
int uniq_verts;
|
||||
int totvert;
|
||||
switch (pbvh->header.type) {
|
||||
case PBVH_GRIDS:
|
||||
totvert = node->prim_indices.size() * pbvh->gridkey.grid_area;
|
||||
uniq_verts = totvert;
|
||||
break;
|
||||
case PBVH_FACES:
|
||||
totvert = node->uniq_verts + node->face_verts;
|
||||
uniq_verts = node->uniq_verts;
|
||||
break;
|
||||
case PBVH_BMESH:
|
||||
totvert = node->bm_unique_verts.size() + node->bm_other_verts.size();
|
||||
uniq_verts = node->bm_unique_verts.size();
|
||||
break;
|
||||
}
|
||||
|
||||
if (pbvh->header.type == PBVH_GRIDS) {
|
||||
vi->key = pbvh->gridkey;
|
||||
|
@ -3134,16 +3109,6 @@ void BKE_pbvh_is_drawing_set(PBVH *pbvh, bool val)
|
|||
pbvh->is_drawing = val;
|
||||
}
|
||||
|
||||
void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop)
|
||||
{
|
||||
UNUSED_VARS(pbvh);
|
||||
BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES);
|
||||
|
||||
if (r_totloop) {
|
||||
*r_totloop = node->loop_indices.size();
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_pbvh_update_active_vcol(PBVH *pbvh, Mesh *mesh)
|
||||
{
|
||||
BKE_pbvh_get_color_layer(mesh, &pbvh->color_layer, &pbvh->color_domain);
|
||||
|
|
|
@ -152,7 +152,14 @@ void gather_group_sizes(OffsetIndices<int> offsets, const IndexMask &mask, Mutab
|
|||
/** Build new offsets that contains only the groups chosen by \a selection. */
|
||||
OffsetIndices<int> gather_selected_offsets(OffsetIndices<int> src_offsets,
|
||||
const IndexMask &selection,
|
||||
int start_offset,
|
||||
MutableSpan<int> dst_offsets);
|
||||
inline OffsetIndices<int> gather_selected_offsets(OffsetIndices<int> src_offsets,
|
||||
const IndexMask &selection,
|
||||
MutableSpan<int> dst_offsets)
|
||||
{
|
||||
return gather_selected_offsets(src_offsets, selection, 0, dst_offsets);
|
||||
}
|
||||
/**
|
||||
* Create a map from indexed elements to the source indices, in other words from the larger array
|
||||
* to the smaller array.
|
||||
|
|
|
@ -816,6 +816,7 @@ class Vector {
|
|||
{
|
||||
const T *prev_end = this->end();
|
||||
end_ = std::remove_if(this->begin(), this->end(), predicate);
|
||||
destruct_n(end_, prev_end - end_);
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
return int64_t(prev_end - end_);
|
||||
}
|
||||
|
|
|
@ -496,7 +496,7 @@ template<typename T> void cdt_draw(const std::string &label, const CDTArrangemen
|
|||
constexpr bool draw_edge_labels = false;
|
||||
constexpr bool draw_face_labels = false;
|
||||
|
||||
if (cdt.verts.size() == 0) {
|
||||
if (cdt.verts.is_empty()) {
|
||||
return;
|
||||
}
|
||||
vec2<double> vmin(DBL_MAX, DBL_MAX);
|
||||
|
|
|
@ -981,7 +981,7 @@ static Array<int> sort_tris_around_edge(
|
|||
* be only 3 or 4 - so OK to make copies of arrays instead of swapping
|
||||
* around in a single array. */
|
||||
const int dbg_level = 0;
|
||||
if (tris.size() == 0) {
|
||||
if (tris.is_empty()) {
|
||||
return Array<int>();
|
||||
}
|
||||
if (dbg_level > 0) {
|
||||
|
@ -1306,7 +1306,7 @@ static bool patch_cell_graph_ok(const CellsInfo &cinfo, const PatchesInfo &pinfo
|
|||
if (cell.merged_to() != NO_INDEX) {
|
||||
continue;
|
||||
}
|
||||
if (cell.patches().size() == 0) {
|
||||
if (cell.patches().is_empty()) {
|
||||
std::cout << "Patch/Cell graph disconnected at Cell " << c << " with no patches\n";
|
||||
return false;
|
||||
}
|
||||
|
@ -2132,7 +2132,7 @@ static void finish_patch_cell_graph(const IMesh &tm,
|
|||
/* For nested components, merge their ambient cell with the nearest containing cell. */
|
||||
Vector<int> outer_components;
|
||||
for (int comp : comp_cont.index_range()) {
|
||||
if (comp_cont[comp].size() == 0) {
|
||||
if (comp_cont[comp].is_empty()) {
|
||||
outer_components.append(comp);
|
||||
}
|
||||
else {
|
||||
|
@ -3217,7 +3217,7 @@ static void do_dissolve(FaceMergeState *fms)
|
|||
dissolve_edges.append(e);
|
||||
}
|
||||
}
|
||||
if (dissolve_edges.size() == 0) {
|
||||
if (dissolve_edges.is_empty()) {
|
||||
return;
|
||||
}
|
||||
/* Things look nicer if we dissolve the longer edges first. */
|
||||
|
|
|
@ -2126,7 +2126,7 @@ static Array<Face *> exact_triangulate_poly(Face *f, IMeshArena *arena)
|
|||
bool needs_steiner = false;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
i_v_out[i] = cdt_out.face[t][i];
|
||||
if (cdt_out.vert_orig[i_v_out[i]].size() == 0) {
|
||||
if (cdt_out.vert_orig[i_v_out[i]].is_empty()) {
|
||||
needs_steiner = true;
|
||||
break;
|
||||
}
|
||||
|
@ -2763,7 +2763,7 @@ static CoplanarClusterInfo find_clusters(const IMesh &tm,
|
|||
if (dbg_level > 0) {
|
||||
std::cout << "found " << maybe_coplanar_tris.size() << " possible coplanar tris\n";
|
||||
}
|
||||
if (maybe_coplanar_tris.size() == 0) {
|
||||
if (maybe_coplanar_tris.is_empty()) {
|
||||
if (dbg_level > 0) {
|
||||
std::cout << "No possible coplanar tris, so no clusters\n";
|
||||
}
|
||||
|
@ -2810,7 +2810,7 @@ static CoplanarClusterInfo find_clusters(const IMesh &tm,
|
|||
no_int_cls.append(&cl);
|
||||
}
|
||||
}
|
||||
if (int_cls.size() == 0) {
|
||||
if (int_cls.is_empty()) {
|
||||
/* t doesn't intersect any existing cluster in its plane, so make one just for it. */
|
||||
if (dbg_level > 1) {
|
||||
std::cout << "no intersecting clusters for t, make a new one\n";
|
||||
|
|
|
@ -50,14 +50,18 @@ void gather_group_sizes(const OffsetIndices<int> offsets,
|
|||
|
||||
OffsetIndices<int> gather_selected_offsets(const OffsetIndices<int> src_offsets,
|
||||
const IndexMask &selection,
|
||||
const int start_offset,
|
||||
MutableSpan<int> dst_offsets)
|
||||
{
|
||||
if (selection.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
BLI_assert(selection.size() == (dst_offsets.size() - 1));
|
||||
gather_group_sizes(src_offsets, selection, dst_offsets);
|
||||
accumulate_counts_to_offsets(dst_offsets);
|
||||
int offset = start_offset;
|
||||
selection.foreach_index_optimized<int>([&](const int i, const int pos) {
|
||||
dst_offsets[pos] = offset;
|
||||
offset += src_offsets[i].size();
|
||||
});
|
||||
dst_offsets.last() = offset;
|
||||
return OffsetIndices<int>(dst_offsets);
|
||||
}
|
||||
|
||||
|
|
|
@ -284,7 +284,7 @@ void graph_draw(const std::string &label,
|
|||
constexpr bool draw_vert_labels = false;
|
||||
constexpr bool draw_edge_labels = false;
|
||||
|
||||
if (verts.size() == 0) {
|
||||
if (verts.is_empty()) {
|
||||
return;
|
||||
}
|
||||
vec2<double> vmin(1e10, 1e10);
|
||||
|
|
|
@ -431,6 +431,17 @@ TEST(vector, RemoveIf)
|
|||
EXPECT_EQ_ARRAY(vec.data(), expected_vec.data(), size_t(vec.size()));
|
||||
}
|
||||
|
||||
TEST(vector, RemoveIfNonTrivialDestructible)
|
||||
{
|
||||
Vector<Vector<int, 0, GuardedAllocator>> vec;
|
||||
for ([[maybe_unused]] const int64_t i : IndexRange(10)) {
|
||||
/* This test relies on leak detection to run after tests. */
|
||||
vec.append(Vector<int, 0, GuardedAllocator>(100));
|
||||
}
|
||||
vec.remove_if([&](const auto & /*value*/) { return true; });
|
||||
EXPECT_TRUE(vec.is_empty());
|
||||
}
|
||||
|
||||
TEST(vector, ExtendSmallVector)
|
||||
{
|
||||
Vector<int> a = {2, 3, 4};
|
||||
|
|
|
@ -1382,7 +1382,11 @@ static void version_geometry_nodes_use_rotation_socket(bNodeTree &ntree)
|
|||
bNodeSocket *socket = nodeFindSocket(node, SOCK_IN, "Rotation");
|
||||
change_input_socket_to_rotation_type(ntree, *node, *socket);
|
||||
}
|
||||
if (STR_ELEM(node->idname, "GeometryNodeDistributePointsOnFaces", "GeometryNodeObjectInfo")) {
|
||||
if (STR_ELEM(node->idname,
|
||||
"GeometryNodeDistributePointsOnFaces",
|
||||
"GeometryNodeObjectInfo",
|
||||
"GeometryNodeInputInstanceRotation"))
|
||||
{
|
||||
bNodeSocket *socket = nodeFindSocket(node, SOCK_OUT, "Rotation");
|
||||
change_output_socket_to_rotation_type(ntree, *node, *socket);
|
||||
}
|
||||
|
@ -2503,12 +2507,6 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 401, 9)) {
|
||||
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
|
||||
if (ntree->type == NTREE_GEOMETRY) {
|
||||
version_geometry_nodes_use_rotation_socket(*ntree);
|
||||
}
|
||||
}
|
||||
|
||||
if (!DNA_struct_member_exists(fd->filesdna, "Material", "char", "displacement_method")) {
|
||||
/* Replace Cycles.displacement_method by Material::displacement_method. */
|
||||
LISTBASE_FOREACH (Material *, material, &bmain->materials) {
|
||||
|
@ -2576,6 +2574,12 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
scene->eevee.ray_tracing_options.resolution_scale = 2;
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
|
||||
if (ntree->type == NTREE_GEOMETRY) {
|
||||
version_geometry_nodes_use_rotation_socket(*ntree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Always run this versioning; meshes are written with the legacy format which always needs to
|
||||
|
|
|
@ -319,25 +319,29 @@ void BM_verts_sort_radial_plane(BMVert **vert_arr, int len)
|
|||
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
|
||||
{
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->vdata, map, src->head.data, &dst->head.data);
|
||||
dst->head.hflag = src->head.hflag & ~BM_ELEM_SELECT;
|
||||
copy_v3_v3(dst->no, src->no);
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMEdge *src, BMEdge *dst)
|
||||
{
|
||||
CustomData_bmesh_copy_block(bm->vdata, map, src->head.data, &dst->head.data);
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->edata, map, src->head.data, &dst->head.data);
|
||||
dst->head.hflag = src->head.hflag & ~BM_ELEM_SELECT;
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMFace *src, BMFace *dst)
|
||||
{
|
||||
CustomData_bmesh_copy_block(bm->vdata, map, src->head.data, &dst->head.data);
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->pdata, map, src->head.data, &dst->head.data);
|
||||
dst->head.hflag = src->head.hflag & ~BM_ELEM_SELECT;
|
||||
copy_v3_v3(dst->no, src->no);
|
||||
dst->mat_nr = src->mat_nr;
|
||||
}
|
||||
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMLoop *src, BMLoop *dst)
|
||||
{
|
||||
CustomData_bmesh_copy_block(bm->vdata, map, src->head.data, &dst->head.data);
|
||||
BLI_assert(src != dst);
|
||||
CustomData_bmesh_copy_block(bm->ldata, map, src->head.data, &dst->head.data);
|
||||
dst->head.hflag = src->head.hflag & ~BM_ELEM_SELECT;
|
||||
}
|
||||
|
||||
|
@ -415,7 +419,7 @@ static BMFace *bm_mesh_copy_new_face(BMesh *bm_new,
|
|||
/* use totface in case adding some faces fails */
|
||||
BM_elem_index_set(f_new, (bm_new->totface - 1)); /* set_inline */
|
||||
|
||||
CustomData_bmesh_copy_block(bm_new->vdata, face_map, f->head.data, &f_new->head.data);
|
||||
CustomData_bmesh_copy_block(bm_new->pdata, face_map, f->head.data, &f_new->head.data);
|
||||
copy_v3_v3(f_new->no, f->no);
|
||||
f_new->mat_nr = f->mat_nr;
|
||||
f_new->head.hflag = f->head.hflag; /* low level! don't do this for normal api use */
|
||||
|
@ -423,7 +427,7 @@ static BMFace *bm_mesh_copy_new_face(BMesh *bm_new,
|
|||
j = 0;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
|
||||
do {
|
||||
CustomData_bmesh_copy_block(bm_new->vdata, loop_map, loops[j]->head.data, &l_iter->head.data);
|
||||
CustomData_bmesh_copy_block(bm_new->ldata, loop_map, loops[j]->head.data, &l_iter->head.data);
|
||||
l_iter->head.hflag = loops[j]->head.hflag & ~BM_ELEM_SELECT;
|
||||
j++;
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
|
|
|
@ -119,7 +119,7 @@ Vector<MemoryBuffer *> ConstantFolder::get_constant_input_buffers(NodeOperation
|
|||
Vector<ConstantOperation *> ConstantFolder::try_fold_operations(Span<NodeOperation *> operations)
|
||||
{
|
||||
Set<NodeOperation *> foldable_ops = find_constant_foldable_operations(operations);
|
||||
if (foldable_ops.size() == 0) {
|
||||
if (foldable_ops.is_empty()) {
|
||||
return Vector<ConstantOperation *>();
|
||||
}
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ const rcti &NodeOperation::get_canvas() const
|
|||
|
||||
void NodeOperation::unset_canvas()
|
||||
{
|
||||
BLI_assert(inputs_.size() == 0);
|
||||
BLI_assert(inputs_.is_empty());
|
||||
flags_.is_canvas_set = false;
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ bool NodeOperation::determine_depending_area_of_interest(rcti *input,
|
|||
ReadBufferOperation *read_operation,
|
||||
rcti *output)
|
||||
{
|
||||
if (inputs_.size() == 0) {
|
||||
if (inputs_.is_empty()) {
|
||||
BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ void MaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
|||
Span<MemoryBuffer *> /*inputs*/)
|
||||
{
|
||||
Vector<MaskRasterHandle *> handles = get_non_null_handles();
|
||||
if (handles.size() == 0) {
|
||||
if (handles.is_empty()) {
|
||||
output->fill(area, COM_VALUE_ZERO);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ set(SRC
|
|||
algorithms/COM_algorithm_symmetric_separable_blur_variable_size.hh
|
||||
algorithms/COM_algorithm_transform.hh
|
||||
|
||||
cached_resources/intern/bokeh_kernel.cc
|
||||
cached_resources/intern/cached_mask.cc
|
||||
cached_resources/intern/cached_shader.cc
|
||||
cached_resources/intern/cached_texture.cc
|
||||
|
@ -94,6 +95,7 @@ set(SRC
|
|||
cached_resources/intern/symmetric_blur_weights.cc
|
||||
cached_resources/intern/symmetric_separable_blur_weights.cc
|
||||
|
||||
cached_resources/COM_bokeh_kernel.hh
|
||||
cached_resources/COM_cached_mask.hh
|
||||
cached_resources/COM_cached_resource.hh
|
||||
cached_resources/COM_cached_shader.hh
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "COM_bokeh_kernel.hh"
|
||||
#include "COM_cached_mask.hh"
|
||||
#include "COM_cached_shader.hh"
|
||||
#include "COM_cached_texture.hh"
|
||||
|
@ -51,6 +52,7 @@ class StaticCacheManager {
|
|||
DistortionGridContainer distortion_grids;
|
||||
KeyingScreenContainer keying_screens;
|
||||
CachedShaderContainer cached_shaders;
|
||||
BokehKernelContainer bokeh_kernels;
|
||||
|
||||
/* Reset the cache manager by deleting the cached resources that are no longer needed because
|
||||
* they weren't used in the last evaluation and prepare the remaining cached resources to track
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_cached_resource.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
class Context;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Bokeh Kernel Key.
|
||||
*/
|
||||
class BokehKernelKey {
|
||||
public:
|
||||
int2 size;
|
||||
int sides;
|
||||
float rotation;
|
||||
float roundness;
|
||||
float catadioptric;
|
||||
float lens_shift;
|
||||
|
||||
BokehKernelKey(
|
||||
int2 size, int sides, float rotation, float roundness, float catadioptric, float lens_shift);
|
||||
|
||||
uint64_t hash() const;
|
||||
};
|
||||
|
||||
bool operator==(const BokehKernelKey &a, const BokehKernelKey &b);
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Bokeh Kernel.
|
||||
*
|
||||
* A cached resource that computes and caches a GPU texture containing the unnormalized convolution
|
||||
* kernel, which when convolved with an image emulates a bokeh lens with the given parameters. */
|
||||
class BokehKernel : public CachedResource {
|
||||
private:
|
||||
GPUTexture *texture_ = nullptr;
|
||||
|
||||
public:
|
||||
BokehKernel(Context &context,
|
||||
int2 size,
|
||||
int sides,
|
||||
float rotation,
|
||||
float roundness,
|
||||
float catadioptric,
|
||||
float lens_shift);
|
||||
|
||||
~BokehKernel();
|
||||
|
||||
void bind_as_texture(GPUShader *shader, const char *texture_name) const;
|
||||
|
||||
void unbind_as_texture() const;
|
||||
|
||||
GPUTexture *texture() const;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Bokeh Kernel Container.
|
||||
*/
|
||||
class BokehKernelContainer : CachedResourceContainer {
|
||||
private:
|
||||
Map<BokehKernelKey, std::unique_ptr<BokehKernel>> map_;
|
||||
|
||||
public:
|
||||
void reset() override;
|
||||
|
||||
/* Check if there is an available BokehKernel cached resource with the given parameters in the
|
||||
* container, if one exists, return it, otherwise, return a newly created one and add it to the
|
||||
* container. In both cases, tag the cached resource as needed to keep it cached for the next
|
||||
* evaluation. */
|
||||
BokehKernel &get(Context &context,
|
||||
int2 size,
|
||||
int sides,
|
||||
float rotation,
|
||||
float roundness,
|
||||
float catadioptric,
|
||||
float lens_shift);
|
||||
};
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -0,0 +1,161 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "COM_bokeh_kernel.hh"
|
||||
#include "COM_context.hh"
|
||||
#include "COM_result.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Bokeh Kernel Key.
|
||||
*/
|
||||
|
||||
BokehKernelKey::BokehKernelKey(
|
||||
int2 size, int sides, float rotation, float roundness, float catadioptric, float lens_shift)
|
||||
: size(size),
|
||||
sides(sides),
|
||||
rotation(rotation),
|
||||
roundness(roundness),
|
||||
catadioptric(catadioptric),
|
||||
lens_shift(lens_shift)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t BokehKernelKey::hash() const
|
||||
{
|
||||
return get_default_hash_3(
|
||||
size, size, get_default_hash(float4(rotation, roundness, catadioptric, lens_shift)));
|
||||
}
|
||||
|
||||
bool operator==(const BokehKernelKey &a, const BokehKernelKey &b)
|
||||
{
|
||||
return a.size == b.size && a.sides == b.sides && a.rotation == b.rotation &&
|
||||
a.roundness == b.roundness && a.catadioptric == b.catadioptric &&
|
||||
a.lens_shift == b.lens_shift;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Bokeh Kernel.
|
||||
*/
|
||||
|
||||
/* The exterior angle is the angle between each two consecutive vertices of the regular polygon
|
||||
* from its center. */
|
||||
static float compute_exterior_angle(int sides)
|
||||
{
|
||||
return (M_PI * 2.0f) / sides;
|
||||
}
|
||||
|
||||
static float compute_rotation(float angle, int sides)
|
||||
{
|
||||
/* Offset the rotation such that the second vertex of the regular polygon lies on the positive
|
||||
* y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming
|
||||
* the first vertex lies on the positive x axis. */
|
||||
const float offset = M_PI_2 - compute_exterior_angle(sides);
|
||||
return angle - offset;
|
||||
}
|
||||
|
||||
BokehKernel::BokehKernel(Context &context,
|
||||
int2 size,
|
||||
int sides,
|
||||
float rotation,
|
||||
float roundness,
|
||||
float catadioptric,
|
||||
float lens_shift)
|
||||
{
|
||||
texture_ = GPU_texture_create_2d(
|
||||
"Bokeh Kernel",
|
||||
size.x,
|
||||
size.y,
|
||||
1,
|
||||
Result::texture_format(ResultType::Color, context.get_precision()),
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
nullptr);
|
||||
|
||||
GPUShader *shader = context.get_shader("compositor_bokeh_image");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
GPU_shader_uniform_1f(shader, "exterior_angle", compute_exterior_angle(sides));
|
||||
GPU_shader_uniform_1f(shader, "rotation", compute_rotation(rotation, sides));
|
||||
GPU_shader_uniform_1f(shader, "roundness", roundness);
|
||||
GPU_shader_uniform_1f(shader, "catadioptric", catadioptric);
|
||||
GPU_shader_uniform_1f(shader, "lens_shift", lens_shift);
|
||||
|
||||
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
|
||||
GPU_texture_image_bind(texture_, image_unit);
|
||||
|
||||
compute_dispatch_threads_at_least(shader, size);
|
||||
|
||||
GPU_texture_image_unbind(texture_);
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
BokehKernel::~BokehKernel()
|
||||
{
|
||||
GPU_texture_free(texture_);
|
||||
}
|
||||
|
||||
void BokehKernel::bind_as_texture(GPUShader *shader, const char *texture_name) const
|
||||
{
|
||||
const int texture_image_unit = GPU_shader_get_sampler_binding(shader, texture_name);
|
||||
GPU_texture_bind(texture_, texture_image_unit);
|
||||
}
|
||||
|
||||
void BokehKernel::unbind_as_texture() const
|
||||
{
|
||||
GPU_texture_unbind(texture_);
|
||||
}
|
||||
|
||||
GPUTexture *BokehKernel::texture() const
|
||||
{
|
||||
return texture_;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Bokeh Kernel Container.
|
||||
*/
|
||||
|
||||
void BokehKernelContainer::reset()
|
||||
{
|
||||
/* First, delete all resources that are no longer needed. */
|
||||
map_.remove_if([](auto item) { return !item.value->needed; });
|
||||
|
||||
/* Second, reset the needed status of the remaining resources to false to ready them to track
|
||||
* their needed status for the next evaluation. */
|
||||
for (auto &value : map_.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
BokehKernel &BokehKernelContainer::get(Context &context,
|
||||
int2 size,
|
||||
int sides,
|
||||
float rotation,
|
||||
float roundness,
|
||||
float catadioptric,
|
||||
float lens_shift)
|
||||
{
|
||||
const BokehKernelKey key(size, sides, rotation, roundness, catadioptric, lens_shift);
|
||||
|
||||
auto &bokeh_kernel = *map_.lookup_or_add_cb(key, [&]() {
|
||||
return std::make_unique<BokehKernel>(
|
||||
context, size, sides, rotation, roundness, catadioptric, lens_shift);
|
||||
});
|
||||
|
||||
bokeh_kernel.needed = true;
|
||||
return bokeh_kernel;
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
|
@ -64,8 +64,8 @@ static void compute_marker_points(MovieClip *movie_clip,
|
|||
Vector<float2> &marker_positions,
|
||||
Vector<float4> &marker_colors)
|
||||
{
|
||||
BLI_assert(marker_positions.size() == 0);
|
||||
BLI_assert(marker_colors.size() == 0);
|
||||
BLI_assert(marker_positions.is_empty());
|
||||
BLI_assert(marker_colors.is_empty());
|
||||
|
||||
ImBuf *image_buffer = BKE_movieclip_get_ibuf(movie_clip, &movie_clip_user);
|
||||
if (!image_buffer) {
|
||||
|
|
|
@ -18,6 +18,7 @@ void StaticCacheManager::reset()
|
|||
distortion_grids.reset();
|
||||
keying_screens.reset();
|
||||
cached_shaders.reset();
|
||||
bokeh_kernels.reset();
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
|
|
@ -23,6 +23,14 @@ void main()
|
|||
|
||||
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
|
||||
|
||||
/* The plane mask is 1 if it is inside the plane and 0 otherwise. However, we use the alpha value
|
||||
* of the sampled color for pixels outside of the plane to utilize the anti-aliasing effect of
|
||||
* the anisotropic filtering. Therefore, the input_tx sampler should use anisotropic filtering
|
||||
* and be clamped to zero border color. */
|
||||
bool is_inside_plane = all(greaterThanEqual(projected_coordinates, vec2(0.0))) &&
|
||||
all(lessThanEqual(projected_coordinates, vec2(1.0)));
|
||||
float mask_value = is_inside_plane ? 1.0 : sampled_color.a;
|
||||
|
||||
imageStore(output_img, texel, sampled_color);
|
||||
imageStore(mask_img, texel, sampled_color.aaaa);
|
||||
imageStore(mask_img, texel, vec4(mask_value));
|
||||
}
|
||||
|
|
|
@ -120,6 +120,10 @@ void DEG_add_scene_relation(DepsNodeHandle *node_handle,
|
|||
Scene *scene,
|
||||
eDepsSceneComponentType component,
|
||||
const char *description);
|
||||
void DEG_add_scene_camera_relation(DepsNodeHandle *node_handle,
|
||||
Scene *scene,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description);
|
||||
void DEG_add_object_relation(DepsNodeHandle *node_handle,
|
||||
Object *object,
|
||||
eDepsObjectComponentType component,
|
||||
|
|
|
@ -83,6 +83,24 @@ void DEG_add_scene_relation(DepsNodeHandle *node_handle,
|
|||
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_scene_camera_relation(DepsNodeHandle *node_handle,
|
||||
Scene *scene,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description)
|
||||
{
|
||||
if (scene->camera != nullptr) {
|
||||
DEG_add_object_relation(node_handle, scene->camera, component, description);
|
||||
}
|
||||
|
||||
/* Like DepsgraphNodeBuilder::build_scene_camera(), we also need to account for other cameras
|
||||
* referenced by markers. */
|
||||
LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
|
||||
if (!ELEM(marker->camera, nullptr, scene->camera)) {
|
||||
DEG_add_object_relation(node_handle, marker->camera, component, description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_add_object_relation(DepsNodeHandle *node_handle,
|
||||
Object *object,
|
||||
eDepsObjectComponentType component,
|
||||
|
|
|
@ -597,6 +597,7 @@ set(GLSL_SRC
|
|||
engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_hybrid_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_occupancy_frag.glsl
|
||||
engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl
|
||||
|
@ -927,8 +928,8 @@ if(WITH_XR_OPENXR)
|
|||
endif()
|
||||
|
||||
if(WITH_GTESTS)
|
||||
if(WITH_OPENGL_DRAW_TESTS)
|
||||
add_definitions(-DWITH_OPENGL_DRAW_TESTS)
|
||||
if(WITH_GPU_DRAW_TESTS)
|
||||
add_definitions(-DWITH_GPU_DRAW_TESTS)
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
add_definitions(-DWITH_OPENGL_BACKEND)
|
||||
endif()
|
||||
|
@ -963,7 +964,7 @@ blender_add_lib(bf_draw "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
|||
|
||||
|
||||
if(WITH_GTESTS)
|
||||
if(WITH_OPENGL_DRAW_TESTS)
|
||||
if(WITH_GPU_DRAW_TESTS)
|
||||
set(TEST_SRC
|
||||
tests/draw_pass_test.cc
|
||||
tests/draw_testing.cc
|
||||
|
|
|
@ -69,11 +69,13 @@ class HiZBuffer {
|
|||
/**
|
||||
* Set source texture for the hiz down-sampling.
|
||||
* Need to be called once at the start of a pipeline or view.
|
||||
* Tag the buffer as dirty.
|
||||
*/
|
||||
void set_source(GPUTexture **texture, int layer = -1)
|
||||
{
|
||||
src_tx_ptr_ = texture;
|
||||
layer_id_ = layer;
|
||||
swap_layer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1012,10 +1012,8 @@ void IrradianceBake::surfels_lights_eval()
|
|||
/* TODO(fclem): Remove this. It is only present to avoid crash inside `shadows.set_view` */
|
||||
inst_.render_buffers.acquire(int2(1));
|
||||
inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
|
||||
inst_.hiz_buffer.set_dirty();
|
||||
|
||||
inst_.lights.set_view(view_z_, grid_pixel_extent_.xy());
|
||||
inst_.shadows.set_view(view_z_);
|
||||
inst_.shadows.set_view(view_z_, inst_.render_buffers.depth_tx);
|
||||
inst_.render_buffers.release();
|
||||
|
||||
inst_.manager->submit(surfel_light_eval_ps_, view_z_);
|
||||
|
|
|
@ -143,6 +143,9 @@ static inline eClosureBits shader_closure_bits_from_flag(const GPUMaterial *gpum
|
|||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_AO)) {
|
||||
closure_bits |= CLOSURE_AMBIENT_OCCLUSION;
|
||||
}
|
||||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) {
|
||||
closure_bits |= CLOSURE_SHADER_TO_RGBA;
|
||||
}
|
||||
return closure_bits;
|
||||
}
|
||||
|
||||
|
|
|
@ -388,7 +388,7 @@ void ForwardPipeline::render(View &view, Framebuffer &prepass_fb, Framebuffer &c
|
|||
|
||||
inst_.hiz_buffer.set_dirty();
|
||||
|
||||
inst_.shadows.set_view(view);
|
||||
inst_.shadows.set_view(view, inst_.render_buffers.depth_tx);
|
||||
inst_.irradiance_cache.set_view(view);
|
||||
|
||||
combined_fb.bind();
|
||||
|
@ -464,8 +464,21 @@ void DeferredLayer::begin_sync()
|
|||
inst_.hiz_buffer.bind_resources(gbuffer_ps_);
|
||||
inst_.cryptomatte.bind_resources(gbuffer_ps_);
|
||||
|
||||
/* Bind light resources for the NPR materials that gets rendered first.
|
||||
* Non-NPR shaders will override these resource bindings. */
|
||||
inst_.lights.bind_resources(gbuffer_ps_);
|
||||
inst_.shadows.bind_resources(gbuffer_ps_);
|
||||
inst_.reflection_probes.bind_resources(gbuffer_ps_);
|
||||
inst_.irradiance_cache.bind_resources(gbuffer_ps_);
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
|
||||
|
||||
gbuffer_single_sided_hybrid_ps_ = &gbuffer_ps_.sub("DoubleSided");
|
||||
gbuffer_single_sided_hybrid_ps_->state_set(state | DRW_STATE_CULL_BACK);
|
||||
|
||||
gbuffer_double_sided_hybrid_ps_ = &gbuffer_ps_.sub("SingleSided");
|
||||
gbuffer_double_sided_hybrid_ps_->state_set(state);
|
||||
|
||||
gbuffer_double_sided_ps_ = &gbuffer_ps_.sub("DoubleSided");
|
||||
gbuffer_double_sided_ps_->state_set(state);
|
||||
|
||||
|
@ -600,9 +613,15 @@ PassMain::Sub *DeferredLayer::material_add(::Material *blender_mat, GPUMaterial
|
|||
eClosureBits closure_bits = shader_closure_bits_from_flag(gpumat);
|
||||
closure_bits_ |= closure_bits;
|
||||
|
||||
PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
|
||||
gbuffer_single_sided_ps_ :
|
||||
gbuffer_double_sided_ps_;
|
||||
bool has_shader_to_rgba = (closure_bits & CLOSURE_SHADER_TO_RGBA) != 0;
|
||||
bool backface_culling = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) != 0;
|
||||
|
||||
PassMain::Sub *pass = (has_shader_to_rgba) ?
|
||||
((backface_culling) ? gbuffer_single_sided_hybrid_ps_ :
|
||||
gbuffer_double_sided_hybrid_ps_) :
|
||||
((backface_culling) ? gbuffer_single_sided_ps_ :
|
||||
gbuffer_double_sided_ps_);
|
||||
|
||||
return &pass->sub(GPU_material_get_name(gpumat));
|
||||
}
|
||||
|
||||
|
@ -620,7 +639,7 @@ void DeferredLayer::render(View &main_view,
|
|||
/* The first pass will never have any surfaces behind it. Nothing is refracted except the
|
||||
* environment. So in this case, disable tracing and fallback to probe. */
|
||||
bool do_screen_space_refraction = !is_first_pass && (closure_bits_ & CLOSURE_REFRACTION);
|
||||
bool do_screen_space_reflection = (closure_bits_ & CLOSURE_REFLECTION);
|
||||
bool do_screen_space_reflection = (closure_bits_ & (CLOSURE_REFLECTION | CLOSURE_DIFFUSE));
|
||||
eGPUTextureUsage usage_rw = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
|
||||
|
||||
if (do_screen_space_reflection) {
|
||||
|
@ -653,10 +672,11 @@ void DeferredLayer::render(View &main_view,
|
|||
inst_.manager->submit(prepass_ps_, render_view);
|
||||
|
||||
inst_.hiz_buffer.swap_layer();
|
||||
/* Update for lighting pass or AO node. */
|
||||
inst_.hiz_buffer.update();
|
||||
|
||||
inst_.irradiance_cache.set_view(render_view);
|
||||
inst_.shadows.set_view(render_view);
|
||||
inst_.shadows.set_view(render_view, inst_.render_buffers.depth_tx);
|
||||
|
||||
if (/* FIXME(fclem): Vulkan doesn't implement load / store config yet. */
|
||||
GPU_backend_get_type() == GPU_BACKEND_VULKAN)
|
||||
|
@ -1195,11 +1215,13 @@ void DeferredProbeLayer::render(View &view,
|
|||
inst_.manager->submit(prepass_ps_, view);
|
||||
|
||||
inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
|
||||
inst_.hiz_buffer.set_dirty();
|
||||
inst_.lights.set_view(view, extent);
|
||||
inst_.shadows.set_view(view);
|
||||
inst_.shadows.set_view(view, inst_.render_buffers.depth_tx);
|
||||
inst_.irradiance_cache.set_view(view);
|
||||
|
||||
/* Update for lighting pass. */
|
||||
inst_.hiz_buffer.update();
|
||||
|
||||
GPU_framebuffer_bind(gbuffer_fb);
|
||||
inst_.manager->submit(gbuffer_ps_, view);
|
||||
|
||||
|
@ -1339,26 +1361,36 @@ PassMain::Sub *PlanarProbePipeline::material_add(::Material *blender_mat, GPUMat
|
|||
return &pass->sub(GPU_material_get_name(gpumat));
|
||||
}
|
||||
|
||||
void PlanarProbePipeline::render(
|
||||
View &view, Framebuffer &gbuffer_fb, Framebuffer &combined_fb, int layer_id, int2 extent)
|
||||
void PlanarProbePipeline::render(View &view,
|
||||
GPUTexture *depth_layer_tx,
|
||||
Framebuffer &gbuffer_fb,
|
||||
Framebuffer &combined_fb,
|
||||
int2 extent)
|
||||
{
|
||||
GPU_debug_group_begin("Planar.Capture");
|
||||
|
||||
inst_.hiz_buffer.set_source(&inst_.planar_probes.depth_tx_, layer_id);
|
||||
inst_.hiz_buffer.set_dirty();
|
||||
|
||||
GPU_framebuffer_bind(gbuffer_fb);
|
||||
GPU_framebuffer_clear_depth(gbuffer_fb, 1.0f);
|
||||
inst_.manager->submit(prepass_ps_, view);
|
||||
|
||||
/* TODO(fclem): This is the only place where we use the layer source to HiZ.
|
||||
* This is because the texture layer view is still a layer texture. */
|
||||
inst_.hiz_buffer.set_source(&depth_layer_tx, 0);
|
||||
inst_.lights.set_view(view, extent);
|
||||
inst_.shadows.set_view(view);
|
||||
inst_.shadows.set_view(view, depth_layer_tx);
|
||||
inst_.irradiance_cache.set_view(view);
|
||||
|
||||
/* Update for lighting pass. */
|
||||
inst_.hiz_buffer.update();
|
||||
|
||||
GPU_framebuffer_bind(gbuffer_fb);
|
||||
GPU_framebuffer_clear_color(gbuffer_fb, float4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
GPU_framebuffer_bind_ex(gbuffer_fb,
|
||||
{
|
||||
{GPU_LOADACTION_LOAD, GPU_STOREACTION_STORE}, /* Depth */
|
||||
{GPU_LOADACTION_CLEAR, GPU_STOREACTION_STORE, {0.0f}}, /* Combined */
|
||||
{GPU_LOADACTION_CLEAR, GPU_STOREACTION_STORE, {0}}, /* GBuf Header */
|
||||
{GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE}, /* GBuf Closure */
|
||||
{GPU_LOADACTION_DONT_CARE, GPU_STOREACTION_STORE}, /* GBuf Color */
|
||||
});
|
||||
inst_.manager->submit(gbuffer_ps_, view);
|
||||
|
||||
GPU_framebuffer_bind(combined_fb);
|
||||
|
|
|
@ -175,6 +175,10 @@ struct DeferredLayerBase {
|
|||
PassMain::Sub *prepass_double_sided_moving_ps_ = nullptr;
|
||||
|
||||
PassMain gbuffer_ps_ = {"Shading"};
|
||||
/* Shaders that use the ClosureToRGBA node needs to be rendered first.
|
||||
* Consider they hybrid forward and deferred. */
|
||||
PassMain::Sub *gbuffer_single_sided_hybrid_ps_ = nullptr;
|
||||
PassMain::Sub *gbuffer_double_sided_hybrid_ps_ = nullptr;
|
||||
PassMain::Sub *gbuffer_single_sided_ps_ = nullptr;
|
||||
PassMain::Sub *gbuffer_double_sided_ps_ = nullptr;
|
||||
|
||||
|
@ -544,8 +548,11 @@ class PlanarProbePipeline : DeferredLayerBase {
|
|||
PassMain::Sub *prepass_add(::Material *material, GPUMaterial *gpumat);
|
||||
PassMain::Sub *material_add(::Material *material, GPUMaterial *gpumat);
|
||||
|
||||
void render(
|
||||
View &view, Framebuffer &gbuffer, Framebuffer &combined_fb, int layer_id, int2 extent);
|
||||
void render(View &view,
|
||||
GPUTexture *depth_layer_tx,
|
||||
Framebuffer &gbuffer,
|
||||
Framebuffer &combined_fb,
|
||||
int2 extent);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -96,6 +96,8 @@ void PlanarProbeModule::end_sync()
|
|||
|
||||
void PlanarProbeModule::set_view(const draw::View &main_view, int2 main_view_extent)
|
||||
{
|
||||
GBuffer &gbuf = instance_.gbuffer;
|
||||
|
||||
const int64_t num_probes = probes_.size();
|
||||
|
||||
/* TODO resolution percentage. */
|
||||
|
@ -111,6 +113,7 @@ void PlanarProbeModule::set_view(const draw::View &main_view, int2 main_view_ext
|
|||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
radiance_tx_.ensure_2d_array(GPU_R11F_G11F_B10F, extent, layer_count, usage);
|
||||
depth_tx_.ensure_2d_array(GPU_DEPTH_COMPONENT32F, extent, layer_count, usage);
|
||||
depth_tx_.ensure_layer_views();
|
||||
|
||||
do_display_draw_ = DRW_state_draw_support() && num_probes > 0;
|
||||
|
||||
|
@ -133,7 +136,6 @@ void PlanarProbeModule::set_view(const draw::View &main_view, int2 main_view_ext
|
|||
world_clip_buf_.plane = probe.reflection_clip_plane_get();
|
||||
world_clip_buf_.push_update();
|
||||
|
||||
GBuffer &gbuf = instance_.gbuffer;
|
||||
gbuf.acquire(extent,
|
||||
instance_.pipelines.deferred.closure_layer_count(),
|
||||
instance_.pipelines.deferred.color_layer_count());
|
||||
|
@ -148,7 +150,7 @@ void PlanarProbeModule::set_view(const draw::View &main_view, int2 main_view_ext
|
|||
GPU_ATTACHMENT_TEXTURE_LAYER(gbuf.closure_tx.layer_view(0), 0));
|
||||
|
||||
instance_.pipelines.planar.render(
|
||||
res.view, res.combined_fb, res.gbuffer_fb, resource_index, extent);
|
||||
res.view, depth_tx_.layer_view(resource_index), res.gbuffer_fb, res.combined_fb, extent);
|
||||
|
||||
if (do_display_draw_ && probe.viewport_display) {
|
||||
display_data_buf_.get_or_resize(display_index++) = {probe.plane_to_world, resource_index};
|
||||
|
@ -157,6 +159,8 @@ void PlanarProbeModule::set_view(const draw::View &main_view, int2 main_view_ext
|
|||
resource_index++;
|
||||
}
|
||||
|
||||
gbuf.release();
|
||||
|
||||
if (resource_index < PLANAR_PROBES_MAX) {
|
||||
/* Tag the end of the array. */
|
||||
probe_planar_buf_[resource_index].layer_id = -1;
|
||||
|
|
|
@ -641,7 +641,12 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
|
|||
info.additional_info("eevee_surf_capture");
|
||||
break;
|
||||
case MAT_PIPE_DEFERRED:
|
||||
info.additional_info("eevee_surf_deferred");
|
||||
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) {
|
||||
info.additional_info("eevee_surf_deferred_hybrid");
|
||||
}
|
||||
else {
|
||||
info.additional_info("eevee_surf_deferred");
|
||||
}
|
||||
break;
|
||||
case MAT_PIPE_FORWARD:
|
||||
info.additional_info("eevee_surf_forward");
|
||||
|
|
|
@ -1187,6 +1187,7 @@ enum eClosureBits : uint32_t {
|
|||
CLOSURE_HOLDOUT = (1u << 10u),
|
||||
CLOSURE_VOLUME = (1u << 11u),
|
||||
CLOSURE_AMBIENT_OCCLUSION = (1u << 12u),
|
||||
CLOSURE_SHADER_TO_RGBA = (1u << 13u),
|
||||
};
|
||||
|
||||
enum GBufferMode : uint32_t {
|
||||
|
@ -1199,7 +1200,7 @@ enum GBufferMode : uint32_t {
|
|||
GBUF_SSS = 4u,
|
||||
|
||||
/** Special configurations. Packs multiple closures into 1 layer. */
|
||||
GBUF_OPAQUE_DIELECTRIC = 4u,
|
||||
GBUF_OPAQUE_DIELECTRIC = 14u,
|
||||
|
||||
/** Set for surfaces without lit closures. This stores only the normal to the surface. */
|
||||
GBUF_UNLIT = 15u,
|
||||
|
|
|
@ -761,8 +761,10 @@ void ShadowModule::init()
|
|||
const int2 atlas_extent = shadow_page_size_ * int2(SHADOW_PAGE_PER_ROW);
|
||||
const int atlas_layers = divide_ceil_u(shadow_page_len_, SHADOW_PAGE_PER_LAYER);
|
||||
|
||||
eGPUTextureUsage tex_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
|
||||
GPU_TEXTURE_USAGE_ATOMIC;
|
||||
eGPUTextureUsage tex_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
|
||||
if (ShadowModule::shadow_technique == ShadowTechnique::ATOMIC_RASTER) {
|
||||
tex_usage |= GPU_TEXTURE_USAGE_ATOMIC;
|
||||
}
|
||||
if (atlas_tx_.ensure_2d_array(atlas_type, atlas_extent, atlas_layers, tex_usage)) {
|
||||
/* Global update. */
|
||||
do_full_update = true;
|
||||
|
@ -824,7 +826,6 @@ void ShadowModule::begin_sync()
|
|||
|
||||
{
|
||||
Manager &manager = *inst_.manager;
|
||||
RenderBuffers &render_buffers = inst_.render_buffers;
|
||||
|
||||
PassMain &pass = tilemap_usage_ps_;
|
||||
pass.init();
|
||||
|
@ -861,7 +862,7 @@ void ShadowModule::begin_sync()
|
|||
sub.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_USAGE_OPAQUE));
|
||||
sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data);
|
||||
sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data);
|
||||
sub.bind_texture("depth_tx", &render_buffers.depth_tx);
|
||||
sub.bind_texture("depth_tx", &src_depth_tx_);
|
||||
sub.push_constant("tilemap_projection_ratio", &tilemap_projection_ratio_);
|
||||
inst_.lights.bind_resources(sub);
|
||||
sub.dispatch(&dispatch_depth_scan_size_);
|
||||
|
@ -1263,15 +1264,15 @@ float ShadowModule::tilemap_pixel_radius()
|
|||
return cubeface_diagonal / pixel_count;
|
||||
}
|
||||
|
||||
/* Update all shadow regions visible inside the view.
|
||||
* If called multiple time for the same view, it will only do the depth buffer scanning
|
||||
* to check any new opaque surfaces.
|
||||
* Needs to be called after `LightModule::set_view();`. */
|
||||
void ShadowModule::set_view(View &view)
|
||||
void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
|
||||
{
|
||||
GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get();
|
||||
|
||||
int3 target_size = inst_.render_buffers.depth_tx.size();
|
||||
src_depth_tx_ = depth_tx;
|
||||
|
||||
int3 target_size(1);
|
||||
GPU_texture_get_mipmap_size(depth_tx, 0, target_size);
|
||||
|
||||
dispatch_depth_scan_size_ = math::divide_ceil(target_size, int3(SHADOW_DEPTH_SCAN_GROUP_SIZE));
|
||||
|
||||
pixel_world_radius_ = screen_pixel_radius(view, int2(target_size));
|
||||
|
|
|
@ -223,6 +223,8 @@ class ShadowModule {
|
|||
|
||||
PassMain::Sub *tilemap_usage_transparent_ps_ = nullptr;
|
||||
GPUBatch *box_batch_ = nullptr;
|
||||
/* Source texture for depth buffer analysis. */
|
||||
GPUTexture *src_depth_tx_ = nullptr;
|
||||
|
||||
Framebuffer usage_tag_fb;
|
||||
|
||||
|
@ -336,7 +338,11 @@ class ShadowModule {
|
|||
|
||||
void set_lights_data();
|
||||
|
||||
void set_view(View &view);
|
||||
/* Update all shadow regions visible inside the view.
|
||||
* If called multiple time for the same view, it will only do the depth buffer scanning
|
||||
* to check any new opaque surfaces.
|
||||
* Needs to be called after `LightModule::set_view();`. */
|
||||
void set_view(View &view, GPUTexture *depth_tx = nullptr);
|
||||
|
||||
void debug_end_sync();
|
||||
void debug_draw(View &view, GPUFrameBuffer *view_fb);
|
||||
|
|
|
@ -49,10 +49,6 @@ class ObjectKey {
|
|||
|
||||
ObjectKey(Object *ob, int sub_key = 0)
|
||||
{
|
||||
/* Since we use `memcmp` for comparison,
|
||||
* we have to ensure the padding bytes are initialized as well. */
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
ob_ = DEG_get_original_object(ob);
|
||||
hash_value_ = BLI_ghashutil_ptrhash(ob_);
|
||||
|
||||
|
@ -81,12 +77,56 @@ class ObjectKey {
|
|||
|
||||
bool operator<(const ObjectKey &k) const
|
||||
{
|
||||
return memcmp(this, &k, sizeof(*this)) < 0;
|
||||
if (hash_value_ != k.hash_value_) {
|
||||
return hash_value_ < k.hash_value_;
|
||||
}
|
||||
if (ob_ != k.ob_) {
|
||||
return (ob_ < k.ob_);
|
||||
}
|
||||
if (parent_ != k.parent_) {
|
||||
return (parent_ < k.parent_);
|
||||
}
|
||||
if (sub_key_ != k.sub_key_) {
|
||||
return (sub_key_ < k.sub_key_);
|
||||
}
|
||||
if (parent_) {
|
||||
for (int i : IndexRange(MAX_DUPLI_RECUR)) {
|
||||
if (id_[i] < k.id_[i]) {
|
||||
return true;
|
||||
}
|
||||
if (id_[i] == INT_MAX) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const ObjectKey &k) const
|
||||
{
|
||||
return memcmp(this, &k, sizeof(*this)) == 0;
|
||||
if (hash_value_ != k.hash_value_) {
|
||||
return false;
|
||||
}
|
||||
if (ob_ != k.ob_) {
|
||||
return false;
|
||||
}
|
||||
if (parent_ != k.parent_) {
|
||||
return false;
|
||||
}
|
||||
if (sub_key_ != k.sub_key_) {
|
||||
return false;
|
||||
}
|
||||
if (parent_) {
|
||||
for (int i : IndexRange(MAX_DUPLI_RECUR)) {
|
||||
if (id_[i] != k.id_[i]) {
|
||||
return false;
|
||||
}
|
||||
if (id_[i] == INT_MAX) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -82,13 +82,14 @@ void ShadingView::render()
|
|||
|
||||
DRW_stats_group_start(name_);
|
||||
|
||||
/* Needs to be before anything else because it query its own gbuffer. */
|
||||
inst_.planar_probes.set_view(render_view_, extent_);
|
||||
|
||||
/* Query temp textures and create frame-buffers. */
|
||||
/* Needs to be before planar_probes because it needs correct crypto-matte & render-pass buffers
|
||||
* to reuse the same deferred shaders. */
|
||||
RenderBuffers &rbufs = inst_.render_buffers;
|
||||
rbufs.acquire(extent_);
|
||||
|
||||
/* Needs to be before anything else because it query its own gbuffer. */
|
||||
inst_.planar_probes.set_view(render_view_, extent_);
|
||||
|
||||
combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx));
|
||||
prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
|
||||
|
@ -116,15 +117,14 @@ void ShadingView::render()
|
|||
GPU_framebuffer_bind(combined_fb_);
|
||||
GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f);
|
||||
|
||||
inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
|
||||
inst_.hiz_buffer.set_dirty();
|
||||
|
||||
inst_.pipelines.background.render(render_view_);
|
||||
|
||||
/* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */
|
||||
inst_.lights.set_view(render_view_, extent_);
|
||||
inst_.reflection_probes.set_view(render_view_);
|
||||
|
||||
inst_.pipelines.background.render(render_view_);
|
||||
|
||||
inst_.hiz_buffer.set_source(&inst_.render_buffers.depth_tx);
|
||||
|
||||
inst_.volume.draw_prepass(render_view_);
|
||||
|
||||
/* TODO(Miguel Pozo): Deferred and forward prepass should happen before the GBuffer pass. */
|
||||
|
@ -211,7 +211,7 @@ void ShadingView::update_view()
|
|||
jitter *= 2.0f;
|
||||
|
||||
window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(jitter));
|
||||
jitter_view_.sync(winmat, winmat);
|
||||
jitter_view_.sync(viewmat, winmat);
|
||||
|
||||
/* FIXME(fclem): The offset may be noticeably large and the culling might make object pop
|
||||
* out of the blurring radius. To fix this, use custom enlarged culling matrix. */
|
||||
|
|
|
@ -44,10 +44,11 @@ void main()
|
|||
|
||||
/* Copy level 0. */
|
||||
ivec2 src_px = ivec2(kernel_origin + local_px) * 2;
|
||||
vec2 samp_co = (vec2(src_px) + 0.5) / vec2(textureSize(depth_tx, 0));
|
||||
#ifdef HIZ_LAYER
|
||||
vec2 samp_co = (vec2(src_px) + 0.5) / vec2(textureSize(depth_layered_tx, 0).xy);
|
||||
vec4 samp = textureGather(depth_layered_tx, vec3(samp_co, float(layer_id)));
|
||||
#else
|
||||
vec2 samp_co = (vec2(src_px) + 0.5) / vec2(textureSize(depth_tx, 0));
|
||||
vec4 samp = textureGather(depth_tx, samp_co);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -8,7 +8,13 @@
|
|||
|
||||
#define EEVEE_SHADOW_LIB
|
||||
|
||||
float shadow_read_depth_at_tilemap_uv(usampler2DArray atlas_tx,
|
||||
#ifdef SHADOW_READ_ATOMIC
|
||||
# define SHADOW_ATLAS_TYPE usampler2DArrayAtomic
|
||||
#else
|
||||
# define SHADOW_ATLAS_TYPE usampler2DArray
|
||||
#endif
|
||||
|
||||
float shadow_read_depth_at_tilemap_uv(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
int tilemap_index,
|
||||
vec2 tilemap_uv)
|
||||
|
@ -86,7 +92,7 @@ float shadow_linear_occluder_distance(LightData light,
|
|||
return receiver_z - occluder_z;
|
||||
}
|
||||
|
||||
ShadowEvalResult shadow_punctual_sample_get(usampler2DArray atlas_tx,
|
||||
ShadowEvalResult shadow_punctual_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
|
@ -116,7 +122,7 @@ ShadowEvalResult shadow_punctual_sample_get(usampler2DArray atlas_tx,
|
|||
return result;
|
||||
}
|
||||
|
||||
ShadowEvalResult shadow_directional_sample_get(usampler2DArray atlas_tx,
|
||||
ShadowEvalResult shadow_directional_sample_get(SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
|
@ -159,7 +165,7 @@ ShadowEvalResult shadow_directional_sample_get(usampler2DArray atlas_tx,
|
|||
}
|
||||
|
||||
ShadowEvalResult shadow_sample(const bool is_directional,
|
||||
usampler2DArray atlas_tx,
|
||||
SHADOW_ATLAS_TYPE atlas_tx,
|
||||
usampler2D tilemaps_tx,
|
||||
LightData light,
|
||||
vec3 P)
|
||||
|
|
|
@ -49,13 +49,16 @@ vec4 closure_to_rgba(Closure cl)
|
|||
vec2 noise_probe = interlieved_gradient_noise(gl_FragCoord.xy, vec2(0, 1), vec2(0.0));
|
||||
LightProbeSample samp = lightprobe_load(g_data.P, g_data.Ng, V);
|
||||
|
||||
diffuse_light += stack.cl[0].light_shadowed;
|
||||
diffuse_light += lightprobe_eval(samp, g_diffuse_data, g_data.P, V, noise_probe);
|
||||
|
||||
reflection_light += stack.cl[1].light_shadowed;
|
||||
reflection_light += lightprobe_eval(samp, g_reflection_data, g_data.P, V, noise_probe);
|
||||
|
||||
vec4 out_color;
|
||||
out_color.rgb = g_emission;
|
||||
out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * stack.cl[0].light_shadowed;
|
||||
out_color.rgb += g_reflection_data.color * g_reflection_data.weight * stack.cl[1].light_shadowed;
|
||||
out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * diffuse_light;
|
||||
out_color.rgb += g_reflection_data.color * g_reflection_data.weight * reflection_light;
|
||||
|
||||
out_color.a = saturate(1.0 - average(g_transmittance));
|
||||
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Deferred lighting evaluation: Lighting is evaluated in a separate pass.
|
||||
*
|
||||
* Outputs shading parameter per pixel using a randomized set of BSDFs.
|
||||
* Some render-pass are written during this pass.
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_ambient_occlusion_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
|
||||
|
||||
vec4 closure_to_rgba(Closure cl)
|
||||
{
|
||||
vec3 diffuse_light = vec3(0.0);
|
||||
vec3 reflection_light = vec3(0.0);
|
||||
vec3 refraction_light = vec3(0.0);
|
||||
float shadow = 1.0;
|
||||
|
||||
float vPz = dot(drw_view_forward(), g_data.P) - dot(drw_view_forward(), drw_view_position());
|
||||
vec3 V = drw_world_incident_vector(g_data.P);
|
||||
|
||||
ClosureLightStack stack;
|
||||
|
||||
ClosureLight cl_diff;
|
||||
cl_diff.N = g_diffuse_data.N;
|
||||
cl_diff.ltc_mat = LTC_LAMBERT_MAT;
|
||||
cl_diff.type = LIGHT_DIFFUSE;
|
||||
stack.cl[0] = cl_diff;
|
||||
|
||||
ClosureLight cl_refl;
|
||||
cl_refl.N = g_reflection_data.N;
|
||||
cl_refl.ltc_mat = LTC_GGX_MAT(dot(g_reflection_data.N, V), g_reflection_data.roughness);
|
||||
cl_refl.type = LIGHT_SPECULAR;
|
||||
stack.cl[1] = cl_refl;
|
||||
|
||||
float thickness = 0.01; /* TODO(fclem) thickness. */
|
||||
light_eval(stack, g_data.P, g_data.Ng, V, vPz, thickness);
|
||||
|
||||
vec2 noise_probe = interlieved_gradient_noise(gl_FragCoord.xy, vec2(0, 1), vec2(0.0));
|
||||
LightProbeSample samp = lightprobe_load(g_data.P, g_data.Ng, V);
|
||||
|
||||
diffuse_light += stack.cl[0].light_shadowed;
|
||||
diffuse_light += lightprobe_eval(samp, g_diffuse_data, g_data.P, V, noise_probe);
|
||||
|
||||
reflection_light += stack.cl[1].light_shadowed;
|
||||
reflection_light += lightprobe_eval(samp, g_reflection_data, g_data.P, V, noise_probe);
|
||||
|
||||
vec4 out_color;
|
||||
out_color.rgb = g_emission;
|
||||
out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * diffuse_light;
|
||||
out_color.rgb += g_reflection_data.color * g_reflection_data.weight * reflection_light;
|
||||
|
||||
out_color.a = saturate(1.0 - average(g_transmittance));
|
||||
|
||||
/* Reset for the next closure tree. */
|
||||
closure_weights_reset();
|
||||
|
||||
return out_color;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
/* Clear AOVs first. In case the material renders to them. */
|
||||
clear_aovs();
|
||||
|
||||
init_globals();
|
||||
|
||||
float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r;
|
||||
g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE));
|
||||
|
||||
fragment_displacement();
|
||||
|
||||
nodetree_surface();
|
||||
|
||||
g_holdout = saturate(g_holdout);
|
||||
|
||||
float thickness = nodetree_thickness();
|
||||
|
||||
g_diffuse_data.color *= g_diffuse_data.weight;
|
||||
g_reflection_data.color *= g_reflection_data.weight;
|
||||
g_refraction_data.color *= g_refraction_data.weight;
|
||||
|
||||
/* TODO(fclem): This feels way too complex for what is it. */
|
||||
bool has_any_bsdf_weight = g_diffuse_data.weight != 0.0 || g_reflection_data.weight != 0.0 ||
|
||||
g_refraction_data.weight != 0.0;
|
||||
vec3 out_normal = has_any_bsdf_weight ? vec3(0.0) : g_data.N;
|
||||
out_normal += g_diffuse_data.N * g_diffuse_data.weight;
|
||||
out_normal += g_reflection_data.N * g_reflection_data.weight;
|
||||
out_normal += g_refraction_data.N * g_refraction_data.weight;
|
||||
out_normal = safe_normalize(out_normal);
|
||||
|
||||
vec3 specular_color = g_reflection_data.color + g_refraction_data.color;
|
||||
|
||||
/* ----- Render Passes output ----- */
|
||||
|
||||
ivec2 out_texel = ivec2(gl_FragCoord.xy);
|
||||
#ifdef MAT_RENDER_PASS_SUPPORT /* Needed because node_tree isn't present in test shaders. */
|
||||
/* Some render pass can be written during the gbuffer pass. Light passes are written later. */
|
||||
if (imageSize(rp_cryptomatte_img).x > 1) {
|
||||
vec4 cryptomatte_output = vec4(
|
||||
cryptomatte_object_buf[resource_id], node_tree.crypto_hash, 0.0);
|
||||
imageStore(rp_cryptomatte_img, out_texel, cryptomatte_output);
|
||||
}
|
||||
output_renderpass_color(uniform_buf.render_pass.normal_id, vec4(out_normal, 1.0));
|
||||
output_renderpass_color(uniform_buf.render_pass.position_id, vec4(g_data.P, 1.0));
|
||||
output_renderpass_color(uniform_buf.render_pass.diffuse_color_id,
|
||||
vec4(g_diffuse_data.color, 1.0));
|
||||
output_renderpass_color(uniform_buf.render_pass.specular_color_id, vec4(specular_color, 1.0));
|
||||
output_renderpass_color(uniform_buf.render_pass.emission_id, vec4(g_emission, 1.0));
|
||||
#endif
|
||||
|
||||
/* ----- GBuffer output ----- */
|
||||
|
||||
GBufferDataPacked gbuf = gbuffer_pack(
|
||||
g_diffuse_data, g_reflection_data, g_refraction_data, out_normal, thickness);
|
||||
|
||||
/* Output header and first closure using frame-buffer attachment. */
|
||||
out_gbuf_header = gbuf.header;
|
||||
out_gbuf_color = gbuf.color[0];
|
||||
out_gbuf_closure = gbuf.closure[0];
|
||||
|
||||
/* Output remaining closures using image store. */
|
||||
/* NOTE: The image view start at layer 1 so all destination layer is `closure_index - 1`. */
|
||||
if (gbuffer_header_unpack(gbuf.header, 1) != GBUF_NONE) {
|
||||
imageStore(out_gbuf_color_img, ivec3(out_texel, 1 - 1), gbuf.color[1]);
|
||||
imageStore(out_gbuf_closure_img, ivec3(out_texel, 1 - 1), gbuf.closure[1]);
|
||||
}
|
||||
if (gbuffer_header_unpack(gbuf.header, 2) != GBUF_NONE) {
|
||||
imageStore(out_gbuf_color_img, ivec3(out_texel, 2 - 1), gbuf.color[2]);
|
||||
imageStore(out_gbuf_closure_img, ivec3(out_texel, 2 - 1), gbuf.closure[2]);
|
||||
}
|
||||
if (gbuffer_header_unpack(gbuf.header, 3) != GBUF_NONE) {
|
||||
/* No color for SSS. */
|
||||
imageStore(out_gbuf_closure_img, ivec3(out_texel, 3 - 1), gbuf.closure[3]);
|
||||
}
|
||||
|
||||
/* ----- Radiance output ----- */
|
||||
|
||||
/* Only output emission during the gbuffer pass. */
|
||||
out_radiance = vec4(g_emission, 0.0);
|
||||
out_radiance.rgb *= 1.0 - g_holdout;
|
||||
out_radiance.a = g_holdout;
|
||||
}
|
|
@ -9,11 +9,9 @@ GPU_SHADER_CREATE_INFO(eevee_hiz_data)
|
|||
.sampler(HIZ_TEX_SLOT, ImageType::FLOAT_2D, "hiz_tx")
|
||||
.additional_info("eevee_global_ubo");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_hiz_update)
|
||||
.do_static_compilation(true)
|
||||
GPU_SHADER_CREATE_INFO(eevee_hiz_update_base)
|
||||
.local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE)
|
||||
.storage_buf(0, Qualifier::READ_WRITE, "uint", "finished_tile_counter")
|
||||
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
|
||||
.image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_0")
|
||||
.image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_1")
|
||||
.image(2, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_2")
|
||||
|
@ -24,12 +22,17 @@ GPU_SHADER_CREATE_INFO(eevee_hiz_update)
|
|||
.push_constant(Type::BOOL, "update_mip_0")
|
||||
.compute_source("eevee_hiz_update_comp.glsl");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_hiz_update)
|
||||
.do_static_compilation(true)
|
||||
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
|
||||
.additional_info("eevee_hiz_update_base");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_hiz_update_layer)
|
||||
.do_static_compilation(true)
|
||||
.define("HIZ_LAYER")
|
||||
.sampler(1, ImageType::DEPTH_2D_ARRAY, "depth_layered_tx")
|
||||
.push_constant(Type::INT, "layer_id")
|
||||
.additional_info("eevee_hiz_update");
|
||||
.additional_info("eevee_hiz_update_base");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_hiz_debug)
|
||||
.do_static_compilation(true)
|
||||
|
|
|
@ -88,7 +88,7 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_light)
|
|||
GPU_SHADER_CREATE_INFO(eevee_surfel_cluster_build)
|
||||
.local_group_size(SURFEL_GROUP_SIZE)
|
||||
.additional_info("eevee_shared", "eevee_surfel_common", "draw_view")
|
||||
.image(0, GPU_R32I, Qualifier::READ_WRITE, ImageType::INT_3D, "cluster_list_img")
|
||||
.image(0, GPU_R32I, Qualifier::READ_WRITE, ImageType::INT_3D_ATOMIC, "cluster_list_img")
|
||||
.compute_source("eevee_surfel_cluster_build_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
||||
|
@ -157,7 +157,7 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_offset)
|
|||
.additional_info("eevee_shared", "eevee_surfel_common", "draw_view")
|
||||
.storage_buf(0, Qualifier::READ, "int", "list_start_buf[]")
|
||||
.storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf")
|
||||
.image(0, GPU_R32I, Qualifier::READ, ImageType::INT_3D, "cluster_list_img")
|
||||
.image(0, GPU_R32I, Qualifier::READ, ImageType::INT_3D_ATOMIC, "cluster_list_img")
|
||||
.image(1, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_3D, "virtual_offset_img")
|
||||
.compute_source("eevee_lightprobe_irradiance_offset_comp.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
|
|
@ -148,7 +148,7 @@ GPU_SHADER_CREATE_INFO(eevee_cryptomatte_out)
|
|||
.storage_buf(CRYPTOMATTE_BUF_SLOT, Qualifier::READ, "vec2", "cryptomatte_object_buf[]")
|
||||
.image_out(RBUFS_CRYPTOMATTE_SLOT, Qualifier::WRITE, GPU_RGBA32F, "rp_cryptomatte_img");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
|
||||
GPU_SHADER_CREATE_INFO(eevee_surf_deferred_base)
|
||||
.define("MAT_DEFERRED")
|
||||
/* NOTE: This removes the possibility of using gl_FragDepth. */
|
||||
.early_fragment_test(true)
|
||||
|
@ -161,7 +161,6 @@ GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
|
|||
* limitation of the number of images we can bind on a single shader. */
|
||||
.image_array_out(GBUF_CLOSURE_SLOT, Qualifier::WRITE, GPU_RGBA16, "out_gbuf_closure_img")
|
||||
.image_array_out(GBUF_COLOR_SLOT, Qualifier::WRITE, GPU_RGB10_A2, "out_gbuf_color_img")
|
||||
.fragment_source("eevee_surf_deferred_frag.glsl")
|
||||
.additional_info("eevee_global_ubo",
|
||||
"eevee_utility_texture",
|
||||
/* Added at runtime because of test shaders not having `node_tree`. */
|
||||
|
@ -170,6 +169,18 @@ GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
|
|||
"eevee_sampling_data",
|
||||
"eevee_hiz_data");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
|
||||
.fragment_source("eevee_surf_deferred_frag.glsl")
|
||||
.additional_info("eevee_surf_deferred_base");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_surf_deferred_hybrid)
|
||||
.fragment_source("eevee_surf_hybrid_frag.glsl")
|
||||
.define("LIGHT_CLOSURE_EVAL_COUNT", "2")
|
||||
.additional_info("eevee_surf_deferred_base",
|
||||
"eevee_light_data",
|
||||
"eevee_lightprobe_data",
|
||||
"eevee_shadow_data");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_surf_forward)
|
||||
.define("MAT_FORWARD")
|
||||
/* Early fragment test is needed for render passes support for forward surfaces. */
|
||||
|
@ -240,7 +251,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_shadow_atomic)
|
|||
.image(SHADOW_ATLAS_IMG_SLOT,
|
||||
GPU_R32UI,
|
||||
Qualifier::READ_WRITE,
|
||||
ImageType::UINT_2D_ARRAY,
|
||||
ImageType::UINT_2D_ARRAY_ATOMIC,
|
||||
"shadow_atlas_img");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_surf_shadow_tbdr)
|
||||
|
@ -294,7 +305,11 @@ GPU_SHADER_CREATE_INFO(eevee_volume_object)
|
|||
Qualifier::READ_WRITE,
|
||||
ImageType::FLOAT_3D,
|
||||
"out_phase_img")
|
||||
.image(VOLUME_OCCUPANCY_SLOT, GPU_R32UI, Qualifier::READ, ImageType::UINT_3D, "occupancy_img")
|
||||
.image(VOLUME_OCCUPANCY_SLOT,
|
||||
GPU_R32UI,
|
||||
Qualifier::READ,
|
||||
ImageType::UINT_3D_ATOMIC,
|
||||
"occupancy_img")
|
||||
.additional_info("eevee_volume_material_common", "draw_object_infos_new", "draw_volume_infos");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_volume_world)
|
||||
|
@ -329,12 +344,12 @@ GPU_SHADER_CREATE_INFO(eevee_surf_occupancy)
|
|||
.image(VOLUME_HIT_COUNT_SLOT,
|
||||
GPU_R32UI,
|
||||
Qualifier::READ_WRITE,
|
||||
ImageType::UINT_2D,
|
||||
ImageType::UINT_2D_ATOMIC,
|
||||
"hit_count_img")
|
||||
.image(VOLUME_OCCUPANCY_SLOT,
|
||||
GPU_R32UI,
|
||||
Qualifier::READ_WRITE,
|
||||
ImageType::UINT_3D,
|
||||
ImageType::UINT_3D_ATOMIC,
|
||||
"occupancy_img")
|
||||
.fragment_source("eevee_surf_occupancy_frag.glsl")
|
||||
.additional_info("eevee_global_ubo", "eevee_sampling_data");
|
||||
|
|
|
@ -205,7 +205,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_clear)
|
|||
.image(SHADOW_ATLAS_IMG_SLOT,
|
||||
GPU_R32UI,
|
||||
Qualifier::READ_WRITE,
|
||||
ImageType::UINT_2D_ARRAY,
|
||||
ImageType::UINT_2D_ARRAY_ATOMIC,
|
||||
"shadow_atlas_img");
|
||||
|
||||
/* TBDR clear implementation. */
|
||||
|
@ -260,6 +260,13 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_tile_store)
|
|||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_shadow_data)
|
||||
/* SHADOW_READ_ATOMIC macro indicating shadow functions should use `usampler2DArrayAtomic` as
|
||||
* the atlas type. */
|
||||
.define("SHADOW_READ_ATOMIC")
|
||||
.sampler(SHADOW_ATLAS_TEX_SLOT, ImageType::UINT_2D_ARRAY_ATOMIC, "shadow_atlas_tx")
|
||||
.sampler(SHADOW_TILEMAPS_TEX_SLOT, ImageType::UINT_2D, "shadow_tilemaps_tx");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(eevee_shadow_data_non_atomic)
|
||||
.sampler(SHADOW_ATLAS_TEX_SLOT, ImageType::UINT_2D_ARRAY, "shadow_atlas_tx")
|
||||
.sampler(SHADOW_TILEMAPS_TEX_SLOT, ImageType::UINT_2D, "shadow_tilemaps_tx");
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ GPU_SHADER_CREATE_INFO(eevee_volume_occupancy_convert)
|
|||
.image(VOLUME_OCCUPANCY_SLOT,
|
||||
GPU_R32UI,
|
||||
Qualifier::READ_WRITE,
|
||||
ImageType::UINT_3D,
|
||||
ImageType::UINT_3D_ATOMIC,
|
||||
"occupancy_img")
|
||||
.fragment_source("eevee_occupancy_convert_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
|
|
@ -256,7 +256,7 @@ template<typename InstanceDataT> struct ShapeInstanceBuf : private select::Selec
|
|||
|
||||
void end_sync(PassSimple &pass, GPUBatch *shape)
|
||||
{
|
||||
if (data_buf.size() == 0) {
|
||||
if (data_buf.is_empty()) {
|
||||
return;
|
||||
}
|
||||
this->select_bind(pass);
|
||||
|
|
|
@ -260,6 +260,7 @@ static void extract_range_iter_looptri_bm(void *__restrict userdata,
|
|||
void *extract_data = tls->userdata_chunk;
|
||||
const MeshRenderData &mr = *data->mr;
|
||||
BMLoop **elt = ((BMLoop * (*)[3]) data->elems)[iter];
|
||||
BLI_assert(iter < mr.edit_bmesh->tottri);
|
||||
for (const ExtractorRunData &run_data : data->extractors) {
|
||||
run_data.extractor->iter_looptri_bm(
|
||||
mr, elt, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
|
||||
|
|
|
@ -409,12 +409,13 @@ void mesh_render_data_update_normals(MeshRenderData &mr, const eMRDataType data_
|
|||
{
|
||||
if (mr.extract_type != MR_EXTRACT_BMESH) {
|
||||
/* Mesh */
|
||||
mr.normals_domain = mr.mesh->normals_domain();
|
||||
mr.vert_normals = mr.mesh->vert_normals();
|
||||
if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
|
||||
mr.face_normals = mr.mesh->face_normals();
|
||||
}
|
||||
if (((data_flag & MR_DATA_LOOP_NOR) &&
|
||||
mr.mesh->normals_domain() == blender::bke::MeshNormalDomain::Corner) ||
|
||||
mr.normals_domain == blender::bke::MeshNormalDomain::Corner) ||
|
||||
(data_flag & MR_DATA_TAN_LOOP_NOR))
|
||||
{
|
||||
mr.loop_normals = mr.mesh->corner_normals();
|
||||
|
|
|
@ -756,7 +756,9 @@ static void draw_subdiv_cache_extra_coarse_face_data_mesh(const MeshRenderData &
|
|||
const blender::OffsetIndices faces = mesh->faces();
|
||||
for (const int i : faces.index_range()) {
|
||||
uint32_t flag = 0;
|
||||
if (!(mr.sharp_faces && mr.sharp_faces[i])) {
|
||||
if (!(mr.normals_domain == blender::bke::MeshNormalDomain::Face ||
|
||||
(mr.sharp_faces && mr.sharp_faces[i])))
|
||||
{
|
||||
flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
|
||||
}
|
||||
if (mr.select_poly && mr.select_poly[i]) {
|
||||
|
@ -785,7 +787,9 @@ static void draw_subdiv_cache_extra_coarse_face_data_mapped(Mesh *mesh,
|
|||
/* Selection and hiding from bmesh. */
|
||||
uint32_t flag = (f) ? compute_coarse_face_flag_bm(f, mr.efa_act) : 0;
|
||||
/* Smooth from mesh. */
|
||||
if (!(mr.sharp_faces && mr.sharp_faces[i])) {
|
||||
if (!(mr.normals_domain == blender::bke::MeshNormalDomain::Face ||
|
||||
(mr.sharp_faces && mr.sharp_faces[i])))
|
||||
{
|
||||
flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
|
||||
}
|
||||
flags_data[i] = uint(faces[i].start()) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
|
||||
|
|
|
@ -3374,7 +3374,7 @@ void DRW_xr_drawing_end()
|
|||
/** \name Internal testing API for gtests
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_OPENGL_DRAW_TESTS
|
||||
#ifdef WITH_GPU_DRAW_TESTS
|
||||
|
||||
void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg)
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OPENGL_DRAW_TESTS
|
||||
#ifdef WITH_GPU_DRAW_TESTS
|
||||
void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -90,9 +90,12 @@ struct MeshRenderData {
|
|||
blender::Span<MLoopTri> looptris;
|
||||
blender::Span<int> looptri_faces;
|
||||
const int *material_indices;
|
||||
|
||||
blender::bke::MeshNormalDomain normals_domain;
|
||||
blender::Span<blender::float3> vert_normals;
|
||||
blender::Span<blender::float3> face_normals;
|
||||
blender::Span<blender::float3> loop_normals;
|
||||
|
||||
const bool *hide_vert;
|
||||
const bool *hide_edge;
|
||||
const bool *hide_poly;
|
||||
|
|
|
@ -68,7 +68,9 @@ static void extract_lnor_iter_face_mesh(const MeshRenderData &mr, const int face
|
|||
if (!mr.loop_normals.is_empty()) {
|
||||
*lnor_data = GPU_normal_convert_i10_v3(mr.loop_normals[ml_index]);
|
||||
}
|
||||
else if (mr.sharp_faces && mr.sharp_faces[face_index]) {
|
||||
else if (mr.normals_domain == bke::MeshNormalDomain::Face ||
|
||||
(mr.sharp_faces && mr.sharp_faces[face_index]))
|
||||
{
|
||||
*lnor_data = GPU_normal_convert_i10_v3(mr.face_normals[face_index]);
|
||||
}
|
||||
else {
|
||||
|
@ -189,7 +191,9 @@ static void extract_lnor_hq_iter_face_mesh(const MeshRenderData &mr,
|
|||
if (!mr.loop_normals.is_empty()) {
|
||||
normal_float_to_short_v3(&lnor_data->x, mr.loop_normals[ml_index]);
|
||||
}
|
||||
else if (mr.sharp_faces && mr.sharp_faces[face_index]) {
|
||||
else if (mr.normals_domain == bke::MeshNormalDomain::Face ||
|
||||
(mr.sharp_faces && mr.sharp_faces[face_index]))
|
||||
{
|
||||
normal_float_to_short_v3(&lnor_data->x, mr.face_normals[face_index]);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -156,29 +156,6 @@ bAction *ED_id_action_ensure(Main *bmain, ID *id)
|
|||
return adt->action;
|
||||
}
|
||||
|
||||
/** Helper for #update_autoflags_fcurve(). */
|
||||
void update_autoflags_fcurve_direct(FCurve *fcu, PropertyRNA *prop)
|
||||
{
|
||||
/* set additional flags for the F-Curve (i.e. only integer values) */
|
||||
fcu->flag &= ~(FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES);
|
||||
switch (RNA_property_type(prop)) {
|
||||
case PROP_FLOAT:
|
||||
/* do nothing */
|
||||
break;
|
||||
case PROP_INT:
|
||||
/* do integer (only 'whole' numbers) interpolation between all points */
|
||||
fcu->flag |= FCURVE_INT_VALUES;
|
||||
break;
|
||||
default:
|
||||
/* do 'discrete' (i.e. enum, boolean values which cannot take any intermediate
|
||||
* values at all) interpolation between all points
|
||||
* - however, we must also ensure that evaluated values are only integers still
|
||||
*/
|
||||
fcu->flag |= (FCURVE_DISCRETE_VALUES | FCURVE_INT_VALUES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, PointerRNA *ptr)
|
||||
{
|
||||
PointerRNA tmp_ptr;
|
||||
|
@ -205,7 +182,7 @@ void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, Poin
|
|||
}
|
||||
|
||||
/* update F-Curve flags */
|
||||
update_autoflags_fcurve_direct(fcu, prop);
|
||||
blender::animrig::update_autoflags_fcurve_direct(fcu, prop);
|
||||
|
||||
if (old_flag != fcu->flag) {
|
||||
/* Same as if keyframes had been changed */
|
||||
|
|
|
@ -24,6 +24,7 @@ set(SRC
|
|||
intern/curves_add.cc
|
||||
intern/curves_attribute_set.cc
|
||||
intern/curves_data.cc
|
||||
intern/curves_draw.cc
|
||||
intern/curves_edit.cc
|
||||
intern/curves_masks.cc
|
||||
intern/curves_ops.cc
|
||||
|
@ -36,6 +37,7 @@ set(LIB
|
|||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::extern::curve_fit_nd
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,6 +6,8 @@
|
|||
* \ingroup edcurves
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
|
||||
#include "ED_curves.hh"
|
||||
|
@ -34,4 +36,187 @@ bool remove_selection(bke::CurvesGeometry &curves, const eAttrDomain selection_d
|
|||
return attributes.domain_size(selection_domain) != domain_size_orig;
|
||||
}
|
||||
|
||||
void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
{
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const VArray<bool> src_cyclic = curves.cyclic();
|
||||
|
||||
Array<bool> points_to_duplicate(curves.points_num());
|
||||
mask.to_bools(points_to_duplicate.as_mutable_span());
|
||||
const int num_points_to_add = mask.size();
|
||||
|
||||
int curr_dst_point_start = 0;
|
||||
Array<int> dst_to_src_point(num_points_to_add);
|
||||
Vector<int> dst_curve_counts;
|
||||
Vector<int> dst_to_src_curve;
|
||||
Vector<bool> dst_cyclic;
|
||||
|
||||
/* Add the duplicated curves and points. */
|
||||
for (const int curve_i : curves.curves_range()) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const Span<bool> curve_points_to_duplicate = points_to_duplicate.as_span().slice(points);
|
||||
const bool curve_cyclic = src_cyclic[curve_i];
|
||||
|
||||
/* Note, these ranges start at zero and needed to be shifted by `points.first()` */
|
||||
const Vector<IndexRange> ranges_to_duplicate = array_utils::find_all_ranges(
|
||||
curve_points_to_duplicate, true);
|
||||
|
||||
if (ranges_to_duplicate.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool is_last_segment_selected = curve_cyclic &&
|
||||
ranges_to_duplicate.first().first() == 0 &&
|
||||
ranges_to_duplicate.last().last() == points.size() - 1;
|
||||
const bool is_curve_self_joined = is_last_segment_selected && ranges_to_duplicate.size() != 1;
|
||||
const bool is_cyclic = ranges_to_duplicate.size() == 1 && is_last_segment_selected;
|
||||
|
||||
const IndexRange range_ids = ranges_to_duplicate.index_range();
|
||||
/* Skip the first range because it is joined to the end of the last range. */
|
||||
for (const int range_i : ranges_to_duplicate.index_range().drop_front(is_curve_self_joined)) {
|
||||
const IndexRange range = ranges_to_duplicate[range_i];
|
||||
|
||||
array_utils::fill_index_range<int>(
|
||||
dst_to_src_point.as_mutable_span().slice(curr_dst_point_start, range.size()),
|
||||
range.start() + points.first());
|
||||
curr_dst_point_start += range.size();
|
||||
|
||||
dst_curve_counts.append(range.size());
|
||||
dst_to_src_curve.append(curve_i);
|
||||
dst_cyclic.append(is_cyclic);
|
||||
}
|
||||
|
||||
/* Join the first range to the end of the last range. */
|
||||
if (is_curve_self_joined) {
|
||||
const IndexRange first_range = ranges_to_duplicate[range_ids.first()];
|
||||
array_utils::fill_index_range<int>(
|
||||
dst_to_src_point.as_mutable_span().slice(curr_dst_point_start, first_range.size()),
|
||||
first_range.start() + points.first());
|
||||
curr_dst_point_start += first_range.size();
|
||||
dst_curve_counts[dst_curve_counts.size() - 1] += first_range.size();
|
||||
}
|
||||
}
|
||||
|
||||
const int old_curves_num = curves.curves_num();
|
||||
const int old_points_num = curves.points_num();
|
||||
const int num_curves_to_add = dst_to_src_curve.size();
|
||||
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
/* Delete selection attribute so that it will not have to be resized. */
|
||||
attributes.remove(".selection");
|
||||
|
||||
curves.resize(old_points_num + num_points_to_add, old_curves_num + num_curves_to_add);
|
||||
|
||||
MutableSpan<int> new_curve_offsets = curves.offsets_for_write();
|
||||
array_utils::copy(dst_curve_counts.as_span(),
|
||||
new_curve_offsets.drop_front(old_curves_num).drop_back(1));
|
||||
offset_indices::accumulate_counts_to_offsets(new_curve_offsets.drop_front(old_curves_num),
|
||||
old_points_num);
|
||||
|
||||
/* Transfer curve and point attributes. */
|
||||
attributes.for_all([&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
|
||||
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
|
||||
if (!attribute) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (meta_data.domain) {
|
||||
case ATTR_DOMAIN_CURVE: {
|
||||
if (id.name() == "cyclic") {
|
||||
return true;
|
||||
}
|
||||
bke::attribute_math::gather(
|
||||
attribute.span,
|
||||
dst_to_src_curve,
|
||||
attribute.span.slice(IndexRange(old_curves_num, num_curves_to_add)));
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
bke::attribute_math::gather(
|
||||
attribute.span,
|
||||
dst_to_src_point,
|
||||
attribute.span.slice(IndexRange(old_points_num, num_points_to_add)));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
attribute.finish();
|
||||
BLI_assert_unreachable();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
attribute.finish();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!(src_cyclic.is_single() && !src_cyclic.get_internal_single())) {
|
||||
array_utils::copy(dst_cyclic.as_span(), curves.cyclic_for_write().drop_front(old_curves_num));
|
||||
}
|
||||
|
||||
curves.update_curve_types();
|
||||
curves.tag_topology_changed();
|
||||
|
||||
bke::SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".selection", ATTR_DOMAIN_POINT);
|
||||
selection.span.take_back(num_points_to_add).fill(true);
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
{
|
||||
const int orig_points_num = curves.points_num();
|
||||
const int orig_curves_num = curves.curves_num();
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
/* Delete selection attribute so that it will not have to be resized. */
|
||||
attributes.remove(".selection");
|
||||
|
||||
/* Resize the curves and copy the offsets of duplicated curves into the new offsets. */
|
||||
curves.resize(curves.points_num(), orig_curves_num + mask.size());
|
||||
const IndexRange orig_curves_range = curves.curves_range().take_front(orig_curves_num);
|
||||
const IndexRange new_curves_range = curves.curves_range().drop_front(orig_curves_num);
|
||||
|
||||
MutableSpan<int> offset_data = curves.offsets_for_write();
|
||||
offset_indices::gather_selected_offsets(
|
||||
OffsetIndices<int>(offset_data.take_front(orig_curves_num + 1)),
|
||||
mask,
|
||||
orig_points_num,
|
||||
offset_data.drop_front(orig_curves_num));
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
|
||||
/* Resize the points array to match the new total point count. */
|
||||
curves.resize(points_by_curve.total_size(), curves.curves_num());
|
||||
|
||||
attributes.for_all([&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
|
||||
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
|
||||
switch (meta_data.domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
bke::attribute_math::gather_group_to_group(points_by_curve.slice(orig_curves_range),
|
||||
points_by_curve.slice(new_curves_range),
|
||||
mask,
|
||||
attribute.span,
|
||||
attribute.span);
|
||||
break;
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
array_utils::gather(attribute.span, mask, attribute.span.take_back(mask.size()));
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return true;
|
||||
}
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
|
||||
curves.update_curve_types();
|
||||
curves.tag_topology_changed();
|
||||
|
||||
bke::SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
|
||||
".selection", ATTR_DOMAIN_CURVE);
|
||||
selection.span.take_back(mask.size()).fill(true);
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
} // namespace blender::ed::curves
|
||||
|
|
|
@ -1244,6 +1244,44 @@ static void CURVES_OT_delete(wmOperatorType *ot)
|
|||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
namespace curves_duplicate {
|
||||
|
||||
static int delete_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
for (Curves *curves_id : get_unique_editable_curves(*C)) {
|
||||
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
|
||||
IndexMaskMemory memory;
|
||||
switch (eAttrDomain(curves_id->selection_domain)) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
duplicate_points(curves, retrieve_selected_points(*curves_id, memory));
|
||||
break;
|
||||
case ATTR_DOMAIN_CURVE:
|
||||
duplicate_curves(curves, retrieve_selected_curves(*curves_id, memory));
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
|
||||
}
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
} // namespace curves_duplicate
|
||||
|
||||
static void CURVES_OT_duplicate(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Duplicate";
|
||||
ot->idname = __func__;
|
||||
ot->description = "Copy selected points or curves";
|
||||
|
||||
ot->exec = curves_duplicate::delete_exec;
|
||||
ot->poll = editable_curves_in_edit_mode_poll;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::curves
|
||||
|
||||
void ED_operatortypes_curves()
|
||||
|
@ -1252,6 +1290,7 @@ void ED_operatortypes_curves()
|
|||
WM_operatortype_append(CURVES_OT_attribute_set);
|
||||
WM_operatortype_append(CURVES_OT_convert_to_particle_system);
|
||||
WM_operatortype_append(CURVES_OT_convert_from_particle_system);
|
||||
WM_operatortype_append(CURVES_OT_draw);
|
||||
WM_operatortype_append(CURVES_OT_snap_curves_to_surface);
|
||||
WM_operatortype_append(CURVES_OT_set_selection_domain);
|
||||
WM_operatortype_append(CURVES_OT_select_all);
|
||||
|
@ -1262,6 +1301,23 @@ void ED_operatortypes_curves()
|
|||
WM_operatortype_append(CURVES_OT_select_less);
|
||||
WM_operatortype_append(CURVES_OT_surface_set);
|
||||
WM_operatortype_append(CURVES_OT_delete);
|
||||
WM_operatortype_append(CURVES_OT_duplicate);
|
||||
}
|
||||
|
||||
void ED_operatormacros_curves()
|
||||
{
|
||||
wmOperatorType *ot;
|
||||
wmOperatorTypeMacro *otmacro;
|
||||
|
||||
/* Duplicate + Move = Interactively place newly duplicated strokes */
|
||||
ot = WM_operatortype_append_macro("CURVES_OT_duplicate_move",
|
||||
"Duplicate",
|
||||
"Make copies of selected elements and move them",
|
||||
OPTYPE_UNDO | OPTYPE_REGISTER);
|
||||
WM_operatortype_macro_define(ot, "CURVES_OT_duplicate");
|
||||
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
|
||||
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
|
||||
RNA_boolean_set(otmacro->ptr, "mirror", false);
|
||||
}
|
||||
|
||||
void ED_keymap_curves(wmKeyConfig *keyconf)
|
||||
|
|
|
@ -249,8 +249,11 @@ static Depsgraph *build_depsgraph_from_indirect_ids(Main &bmain,
|
|||
{
|
||||
Set<ID *> ids_for_relations;
|
||||
bool needs_own_transform_relation = false;
|
||||
nodes::find_node_tree_dependencies(
|
||||
node_tree_orig, ids_for_relations, needs_own_transform_relation);
|
||||
bool needs_scene_camera_relation = false;
|
||||
nodes::find_node_tree_dependencies(node_tree_orig,
|
||||
ids_for_relations,
|
||||
needs_own_transform_relation,
|
||||
needs_scene_camera_relation);
|
||||
IDP_foreach_property(
|
||||
&const_cast<IDProperty &>(properties),
|
||||
IDP_TYPE_FILTER_ID,
|
||||
|
|
|
@ -616,7 +616,7 @@ static bke::CurvesGeometry remove_points_and_split(const bke::CurvesGeometry &cu
|
|||
const Vector<IndexRange> ranges_to_keep = array_utils::find_all_ranges(curve_points_to_delete,
|
||||
false);
|
||||
|
||||
if (ranges_to_keep.size() == 0) {
|
||||
if (ranges_to_keep.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -676,7 +676,6 @@ static bke::CurvesGeometry remove_points_and_split(const bke::CurvesGeometry &cu
|
|||
|
||||
static int grease_pencil_delete_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
using namespace blender;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
Object *object = CTX_data_active_object(C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
|
@ -1116,11 +1115,11 @@ static void GREASE_PENCIL_OT_cyclical_set(wmOperatorType *ot)
|
|||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Set selected material as active material
|
||||
/** \name Set Active Material Operator
|
||||
* \{ */
|
||||
|
||||
static int grease_pencil_set_active_material_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
using namespace blender;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
Object *object = CTX_data_active_object(C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
|
@ -1164,7 +1163,7 @@ static void GREASE_PENCIL_OT_set_active_material(wmOperatorType *ot)
|
|||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Set stroke uniform Thickness
|
||||
/** \name Set Uniform Thickness Operator
|
||||
* \{ */
|
||||
|
||||
static int grease_pencil_set_uniform_thickness_exec(bContext *C, wmOperator *op)
|
||||
|
@ -1222,7 +1221,7 @@ static void GREASE_PENCIL_OT_set_uniform_thickness(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Set stroke uniform Opacity
|
||||
/** \name Set Uniform Opacity Operator
|
||||
* \{ */
|
||||
|
||||
static int grease_pencil_set_uniform_opacity_exec(bContext *C, wmOperator *op)
|
||||
|
@ -1330,7 +1329,7 @@ static void GREASE_PENCIL_OT_stroke_switch_direction(wmOperatorType *ot)
|
|||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Caps mode Set Operator
|
||||
/** \name Set Curve Caps Operator
|
||||
* \{ */
|
||||
|
||||
enum class CapsMode : int8_t {
|
||||
|
@ -1456,6 +1455,7 @@ static void GREASE_PENCIL_OT_caps_set(wmOperatorType *ot)
|
|||
/* -------------------------------------------------------------------- */
|
||||
/** \name Set Active Material Operator
|
||||
* \{ */
|
||||
|
||||
/* Retry enum items with object materials. */
|
||||
static const EnumPropertyItem *material_enum_itemf(bContext *C,
|
||||
PointerRNA * /*ptr*/,
|
||||
|
@ -1530,153 +1530,31 @@ static void GREASE_PENCIL_OT_set_material(wmOperatorType *ot)
|
|||
/** \name Duplicate Operator
|
||||
* \{ */
|
||||
|
||||
static void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask)
|
||||
{
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const VArray<bool> src_cyclic = curves.cyclic();
|
||||
|
||||
Array<bool> points_to_duplicate(curves.points_num());
|
||||
mask.to_bools(points_to_duplicate.as_mutable_span());
|
||||
const int num_points_to_add = mask.size();
|
||||
|
||||
int curr_dst_point_start = 0;
|
||||
Array<int> dst_to_src_point(num_points_to_add);
|
||||
Vector<int> dst_curve_counts;
|
||||
Vector<int> dst_to_src_curve;
|
||||
Vector<bool> dst_cyclic;
|
||||
|
||||
/* Add the duplicated curves and points. */
|
||||
for (const int curve_i : curves.curves_range()) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const Span<bool> curve_points_to_duplicate = points_to_duplicate.as_span().slice(points);
|
||||
const bool curve_cyclic = src_cyclic[curve_i];
|
||||
|
||||
/* Note, these ranges start at zero and needed to be shifted by `points.first()` */
|
||||
const Vector<IndexRange> ranges_to_duplicate = array_utils::find_all_ranges(
|
||||
curve_points_to_duplicate, true);
|
||||
|
||||
if (ranges_to_duplicate.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool is_last_segment_selected = curve_cyclic &&
|
||||
ranges_to_duplicate.first().first() == 0 &&
|
||||
ranges_to_duplicate.last().last() == points.size() - 1;
|
||||
const bool is_curve_self_joined = is_last_segment_selected && ranges_to_duplicate.size() != 1;
|
||||
const bool is_cyclic = ranges_to_duplicate.size() == 1 && is_last_segment_selected;
|
||||
|
||||
const IndexRange range_ids = ranges_to_duplicate.index_range();
|
||||
/* Skip the first range because it is joined to the end of the last range. */
|
||||
for (const int range_i : ranges_to_duplicate.index_range().drop_front(is_curve_self_joined)) {
|
||||
const IndexRange range = ranges_to_duplicate[range_i];
|
||||
|
||||
array_utils::fill_index_range<int>(
|
||||
dst_to_src_point.as_mutable_span().slice(curr_dst_point_start, range.size()),
|
||||
range.start() + points.first());
|
||||
curr_dst_point_start += range.size();
|
||||
|
||||
dst_curve_counts.append(range.size());
|
||||
dst_to_src_curve.append(curve_i);
|
||||
dst_cyclic.append(is_cyclic);
|
||||
}
|
||||
|
||||
/* Join the first range to the end of the last range. */
|
||||
if (is_curve_self_joined) {
|
||||
const IndexRange first_range = ranges_to_duplicate[range_ids.first()];
|
||||
array_utils::fill_index_range<int>(
|
||||
dst_to_src_point.as_mutable_span().slice(curr_dst_point_start, first_range.size()),
|
||||
first_range.start() + points.first());
|
||||
curr_dst_point_start += first_range.size();
|
||||
dst_curve_counts[dst_curve_counts.size() - 1] += first_range.size();
|
||||
}
|
||||
}
|
||||
|
||||
const int old_curves_num = curves.curves_num();
|
||||
const int old_points_num = curves.points_num();
|
||||
const int num_curves_to_add = dst_to_src_curve.size();
|
||||
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
||||
/* Delete selection attribute so that it will not have to be resized. */
|
||||
attributes.remove(".selection");
|
||||
|
||||
curves.resize(old_points_num + num_points_to_add, old_curves_num + num_curves_to_add);
|
||||
|
||||
MutableSpan<int> new_curve_offsets = curves.offsets_for_write();
|
||||
array_utils::copy(dst_curve_counts.as_span(),
|
||||
new_curve_offsets.drop_front(old_curves_num).drop_back(1));
|
||||
offset_indices::accumulate_counts_to_offsets(new_curve_offsets.drop_front(old_curves_num),
|
||||
old_points_num);
|
||||
|
||||
/* Transfer curve and point attributes. */
|
||||
attributes.for_all([&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
|
||||
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
|
||||
if (!attribute) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (meta_data.domain) {
|
||||
case ATTR_DOMAIN_CURVE: {
|
||||
if (id.name() == "cyclic") {
|
||||
return true;
|
||||
}
|
||||
bke::attribute_math::gather(
|
||||
attribute.span,
|
||||
dst_to_src_curve,
|
||||
attribute.span.slice(IndexRange(old_curves_num, num_curves_to_add)));
|
||||
break;
|
||||
}
|
||||
case ATTR_DOMAIN_POINT: {
|
||||
bke::attribute_math::gather(
|
||||
attribute.span,
|
||||
dst_to_src_point,
|
||||
attribute.span.slice(IndexRange(old_points_num, num_points_to_add)));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
attribute.finish();
|
||||
BLI_assert_unreachable();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
attribute.finish();
|
||||
|
||||
return true;
|
||||
});
|
||||
array_utils::copy(dst_cyclic.as_span(), curves.cyclic_for_write().drop_front(old_curves_num));
|
||||
|
||||
curves.update_curve_types();
|
||||
curves.tag_topology_changed();
|
||||
|
||||
/* Deselect the original and select the new curves. */
|
||||
bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
|
||||
curves, ATTR_DOMAIN_CURVE, CD_PROP_BOOL);
|
||||
curves::fill_selection_true(selection.span,
|
||||
IndexMask(IndexRange(old_curves_num, num_curves_to_add)));
|
||||
curves::fill_selection_false(selection.span, IndexMask(old_curves_num));
|
||||
selection.finish();
|
||||
}
|
||||
|
||||
static int grease_pencil_duplicate_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
using namespace blender;
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
Object *object = CTX_data_active_object(C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
|
||||
const eAttrDomain selection_domain = ED_grease_pencil_selection_domain_get(scene->toolsettings);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Array<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
|
||||
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask points = ed::greasepencil::retrieve_editable_and_selected_points(
|
||||
*object, info.drawing, memory);
|
||||
if (points.is_empty()) {
|
||||
const IndexMask elements = retrieve_editable_and_selected_elements(
|
||||
*object, info.drawing, selection_domain, memory);
|
||||
if (elements.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
|
||||
duplicate_points(curves, points);
|
||||
if (selection_domain == ATTR_DOMAIN_CURVE) {
|
||||
curves::duplicate_curves(curves, elements);
|
||||
}
|
||||
else if (selection_domain == ATTR_DOMAIN_POINT) {
|
||||
curves::duplicate_points(curves, elements);
|
||||
}
|
||||
info.drawing.tag_topology_changed();
|
||||
changed.store(true, std::memory_order_relaxed);
|
||||
});
|
||||
|
|
|
@ -32,6 +32,7 @@ struct wmKeyConfig;
|
|||
* \{ */
|
||||
|
||||
void ED_operatortypes_curves();
|
||||
void ED_operatormacros_curves();
|
||||
void ED_curves_undosys_type(UndoType *ut);
|
||||
void ED_keymap_curves(wmKeyConfig *keyconf);
|
||||
|
||||
|
@ -75,6 +76,7 @@ bool curves_poll(bContext *C);
|
|||
* \{ */
|
||||
|
||||
void CURVES_OT_attribute_set(wmOperatorType *ot);
|
||||
void CURVES_OT_draw(wmOperatorType *ot);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@ -285,6 +287,9 @@ bool select_circle(const ViewContext &vc,
|
|||
*/
|
||||
bool remove_selection(bke::CurvesGeometry &curves, eAttrDomain selection_domain);
|
||||
|
||||
void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask);
|
||||
void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::ed::curves
|
||||
|
|
|
@ -63,7 +63,6 @@ bAction *ED_id_action_ensure(Main *bmain, ID *id);
|
|||
* but also through RNA when editing an ID prop, see #37103).
|
||||
*/
|
||||
void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, PointerRNA *ptr);
|
||||
void update_autoflags_fcurve_direct(FCurve *fcu, PropertyRNA *prop);
|
||||
|
||||
/* -------- */
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue