Add support to Drag and Drop to FileHandlers #116047

Merged
Jesse Yurkovich merged 16 commits from guishe/blender:fh-dnd-support into main 2024-01-06 03:51:57 +01:00
186 changed files with 4806 additions and 1125 deletions
Showing only changes of commit 47bf7e1eba - Show all commits

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,7 +60,7 @@ class CPUKernels {
int x,
int y,
float threshold,
bool reset,
int reset,
int offset,
int stride)>;

View File

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

View File

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

View File

@ -103,7 +103,7 @@ class DenoiserGPU : public Denoiser {
int denoised_offset;
int num_components;
bool use_compositing;
int use_compositing;
bool use_denoising_albedo;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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. */

View File

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

373
release/license/MPL-2.0.txt Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
/** \} */

View File

@ -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]);

View File

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

View File

@ -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 = ';';

View File

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

View File

@ -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();

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

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

View File

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

View File

@ -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_);
}

View File

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

View File

@ -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. */

View File

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

View File

@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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 *>();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,6 +18,7 @@ void StaticCacheManager::reset()
distortion_grids.reset();
keying_screens.reset();
cached_shaders.reset();
bokeh_kernels.reset();
}
} // namespace blender::realtime_compositor

View File

@ -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));
}

View File

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

View File

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

View File

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

View File

@ -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();
}
/**

View File

@ -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_);

View File

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

View File

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

View File

@ -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);
};
/** \} */

View File

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

View File

@ -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");

View File

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

View File

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

View File

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

View File

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

View File

@ -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. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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");

View File

@ -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");

View File

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

View File

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

View File

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

View File

@ -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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
});

View File

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

View File

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