GPv3: Initial sculpt mode #119338

Merged
Lukas Tönne merged 28 commits from LukasTonne/blender:gp3-sculpt-mode into main 2024-04-11 09:40:00 +02:00
112 changed files with 2312 additions and 518 deletions
Showing only changes of commit 650d285fc2 - Show all commits

View File

@ -176,7 +176,6 @@ def main() -> None:
# Support version without a minor version "3" (add zero).
tuple((0, 0, 0))
)
python_version_str = "%d.%d" % python_version_number[:2]
# Get Blender version.
blender_version_str = str(make_utils.parse_blender_version())

View File

@ -9,4 +9,5 @@ requests==2.31.0
# Only needed to match the theme used for the official documentation.
# Without this theme, the default theme will be used.
sphinx_rtd_theme==1.3.0rc1
furo==2024.1.29
sphinx-basic-ng==1.0.0b2

View File

@ -1929,22 +1929,30 @@ def write_sphinx_conf_py(basepath):
# The theme 'sphinx_rtd_theme' is no longer distributed with sphinx by default, only use when available.
fw(r"""
try:
__import__('sphinx_rtd_theme')
html_theme = 'sphinx_rtd_theme'
import furo
html_theme = "furo"
del furo
except ModuleNotFoundError:
pass
""")
if html_theme == "furo":
html_theme_options = {
"light_css_variables": {
"color-brand-primary": "#265787",
"color-brand-content": "#265787",
},
}
fw("if html_theme == 'sphinx_rtd_theme':\n")
fw(" html_theme_options = {\n")
fw(" 'display_version': False,\n")
# fw(" 'analytics_id': '',\n")
# fw(" 'collapse_navigation': True,\n")
fw(" 'sticky_navigation': False,\n")
fw(" 'navigation_depth': 1,\n")
fw(" 'includehidden': False,\n")
# fw(" 'titles_only': False\n")
fw(" }\n\n")
html_sidebars = {
"**": [
"sidebar/brand.html",
"sidebar/search.html",
"sidebar/scroll-start.html",
"sidebar/navigation.html",
"sidebar/scroll-end.html",
# "sidebar/variant-selector.html",
]
}
""")
# not helpful since the source is generated, adds to upload size.
fw("html_copy_source = False\n")
@ -1961,7 +1969,7 @@ except ModuleNotFoundError:
fw("html_logo = 'static/blender_logo.svg'\n")
# Disable default `last_updated` value, since this is the date of doc generation, not the one of the source commit.
fw("html_last_updated_fmt = None\n\n")
fw("if html_theme == 'sphinx_rtd_theme':\n")
fw("if html_theme == 'furo':\n")
fw(" html_css_files = ['css/version_switch.css']\n")
fw(" html_js_files = ['js/version_switch.js']\n")

View File

@ -1,19 +1,323 @@
/* Hide home icon in search area */
.wy-side-nav-search > a:hover {background: none; opacity: 0.9}
.wy-side-nav-search > a.icon::before {content: none}
/*
* This stylesheet is applied after the theme's default one,
* and thus any overrides or additions can be added here.
*
* More info:
* https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx.application.Sphinx.add_css_file
*/
.wy-nav-content {
max-width: 1000px !important;
body {
--sidebar-caption-font-size: var(--font-size--normal);
--toc-title-font-size: var(--font-size--normal);
--toc-font-size: var(--sidebar-item-font-size);
--admonition-font-size: var(--font-size--normal);
--admonition-title-font-size: var(--font-size--normal);
--color-api-name: #e87d0d;
}
/* Fix long titles on mobile */
h1, h2, h3, h4, h5, h6 {word-break: break-all}
h1,
h2,
h3 {
margin-top: 1.75rem;
margin-bottom: 1rem;
}
/* Temp fix for https://github.com/readthedocs/sphinx_rtd_theme/pull/1109 */
.hlist tr {
display: -ms-flexbox;
display: flex;
flex-flow: row wrap;
}
h1 {
font-size: 2em;
}
.hlist td {margin-right: auto}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.25em;
}
h4,
h5,
h6,
.rubric {
margin-top: 1.25rem;
margin-bottom: 0.75rem;
font-size: 1.125em;
}
/* Reduce the margins on top/bottom of horizontal lines. */
hr.docutils {
margin: 1rem 0;
}
/* Slightly decrease text to make the text fit on one line */
.sidebar-brand-text {
font-size: 1.4rem;
}
.toctree-checkbox~label .icon svg {
transition: transform 0.25s ease-out;
}
/* Add more visual weight to definition terms */
dl dt {
font-weight: bold !important
}
/* Fixes to field list, see #104636 */
dl.field-list {
display: grid;
grid-template-columns: auto minmax(80%, 95%);
p {
margin-bottom: 0;
}
}
/* TABLE & FIGURE */
/* Cell's vertical align. */
/* use "valign" class for middle align */
table.docutils:not(.valign) td {
vertical-align: baseline;
}
/* Decrease whitespace above figure and add it below */
figure {
padding-bottom: 0.5rem;
}
figcaption {
margin-bottom: 0.5rem !important;
p {
margin-top: 0;
}
}
/* End TABLE & FIGURE. */
/* Force admonition to span the full width if close to a figure */
.admonition {
clear: both;
}
/* Use secondary font color for caption text */
figcaption,
caption {
color: var(--color-foreground-secondary);
font-size: var(--font-size--small)
}
/* A bit hacky, revert the themes styling of kbd */
kbd:not(.compound) {
all: revert;
}
/* Only style parent kbd elements instead of the individual children */
:not(dl.option-list)> :not(kbd):not(kbd)>kbd,
.menuselection {
background-color: var(--color-background-secondary);
border: 1px solid var(--color-foreground-border);
border-radius: .2rem;
box-shadow: 0 .0625rem 0 rgba(0, 0, 0, .2), inset 0 0 0 .125rem var(--color-background-secondary);
color: var(--color-foreground-primary);
display: inline-block;
margin: 0;
padding: 0 .2rem;
}
.highlight .nc,
.highlight .nn,
.highlight .gu {
text-decoration-line: none !important;
}
.caption .menuselection {
background-color: transparent;
border: none;
}
a {
text-decoration: none;
}
/* Quotes for Fig. "link". */
a[href^="#fig-"]::before {
content: "\201c";
}
a[href^="#fig-"]::after {
content: "\201d";
}
/* Mark external links. */
a.external {
filter: brightness(150%);
}
/* List blender.org as internal. */
.external[href^="https://www.blender.org"],
.external[href^="https://docs.blender.org"],
.external[href^="https://projects.blender.org"],
.external[href^="https://builder.blender.org"],
.external[href^="https://code.blender.org"],
.external[href^="https://translate.blender.org"],
.external[href^="https://fund.blender.org"],
.external[href^="blender_manual_html.zip"],
.external[href^="blender_manual_epub.zip"],
.external[href^="https://archive.blender.org"] {
filter: revert;
}
/* ".. container::" lead, block text float around image. */
.lead {
clear: both;
width: 100%;
}
/* Start reference admonition. */
.admonition.refbox {
border-color: rgb(50, 50, 50);
}
.admonition.refbox>.admonition-title {
background-color: rgba(50, 50, 50, 0.2);
border-color: rgb(50, 50, 50);
}
.admonition.refbox>.admonition-title::before {
background-color: var(--color-content-foreground);
}
/* 'refbox' field. */
.refbox .field-list .field-name,
.refbox .field-list .field-body {
padding: 0px;
}
.refbox dl dt {
font-weight: normal
}
/* End reference admonition. */
/* Applied on main index:sections. */
.global-index-toc {
display: none;
}
/* Start section cards. */
.toc-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
grid-gap: 20px;
list-style-type: none;
margin-bottom: 24px;
}
.card {
border-radius: .3em;
user-select: none;
}
.card div.figure,
.card figure {
margin-bottom: 0px;
display: block;
}
.card img {
border-top-left-radius: .3em;
border-top-right-radius: .3em;
}
.card dl {
margin-bottom: 10px
}
.card dl dt>a {
display: block;
width: 100%;
margin-bottom: 10px;
}
.card dl dt a em,
.card dl dt a span {
font-weight: bold;
font-style: normal;
font-size: 1.3em;
}
.card dl dt {
padding: 0px 15px 0px !important
}
.card dl dd {
padding: 0px 15px 5px 15px;
font-style: normal;
margin: 0px;
color: var(--color-foreground-secondary);
font-size: 90%;
}
.card {
box-shadow: 0 .2rem .5rem rgba(0, 0, 0, .05), 0 0 .0625rem rgba(0, 0, 0, .1);
}
#getting-started .card {
box-shadow: none;
}
/* End section cards. */
/* Start custom toctree. */
/* Indent all lines following the first. */
.toctree-wrapper * a {
display: block;
padding-top: 0.25em;
}
.toctree-wrapper ul {
list-style: none;
padding-left: 0;
}
/* Underline provided by nested ul (not li). */
.toctree-wrapper * ul {
margin-bottom: 1rem !important;
border-top: solid var(--color-background-border) 1px;
padding-left: 2em;
}
/* End custom toctree. */
/* Start footer contribute link */
.footer-contribute {
display: block;
font-size: var(--font-size--small);
}
.bottom-of-page {
padding-bottom: 0;
}
.footer-contribute ul {
margin: 0;
padding: 0;
padding-bottom: 1rem
}
.footer-contribute li {
display: inline;
list-style-type: none;
padding-right: 1.5rem;
}
@media print {
.footer-contribute {
display: none;
}
}
/* End footer contribute link */

View File

@ -1,49 +1,35 @@
/* Override RTD theme */
.rst-versions {
border-top: 0px;
overflow: visible;
}
.version-btn.vdeact {
cursor: default;
color: dimgray;
}
.version-btn.vdeact::after {
content: "";
}
#versionwrap {
margin: 0;
display: flex;
padding-top: 2px;
font-size: 90%;
padding-left: 0;
font-size: var(--sidebar-item-font-size);
justify-content: center;
flex-wrap: wrap;
}
#versionwrap>ul {
list-style: none;
}
#versionwrap>li {
display: flex;
width: 50%;
}
.version-btn {
display: inline-block;
background-color: #272525;
width: 140px;
background-color: var(--color-sidebar-background);
width: 100%;
text-align: center;
padding: 3px 10px;
margin: 0px 5px 4px;
vertical-align: middle;
color: #27AE60;
border: solid 1px #444444;
color: var(--color-link);
border: solid 1px var(--color-sidebar-background-border);
border-radius: 3px;
cursor: pointer;
z-index: 400;
transition: border-color 0.4s;
}
.version-btn::after {
content: "\f0d8";
display: inline;
font: normal normal normal 16px/1 FontAwesome;
color: #8d8c8c;
vertical-align: top;
padding-left: 0.5em;
}
.version-btn-open::after {
@ -57,7 +43,7 @@
.version-btn-open {
color: gray;
border: solid 1px gray;
border: solid 1px var(--color-sidebar-background-border);
}
.version-btn.wait {
@ -73,32 +59,34 @@
display: none;
position: absolute;
bottom: 28px;
width: 140px;
width: 50%;
margin: 0 5px;
padding-bottom: 4px;
background-color: #0003;
border-radius: 3px;
box-shadow: 0 0 6px #000C;
z-index: 999;
max-height: calc(100vh - 30px);
overflow-x: clip;
overflow-y: auto;
cursor: default;
}
.version-title {
padding: 5px;
color: black;
color: var(--color-content-foreground);
text-align: center;
font-size: 102%;
background-color: #27ae60;
border-bottom: solid 1.5px #444;
font-weight: 700;
background-color: var(--color-brand-primary);
border-bottom: solid 1.5px var(--color-sidebar-background-border);
}
.version-list {
padding-left: 0;
margin-top: 0;
margin-bottom: 4px;
text-align: center;
background-color: #000C;
border: solid 1px gray;
border: solid 1px var(--color-sidebar-background-border);
border-radius: 0px 0px 3px 3px;
}
@ -112,39 +100,21 @@
width: 100%;
margin: 0;
padding: 4px 0px;
color: #404040;
color: var(--color-sidebar-link-text);
}
.version-list li {
background-color: #ede9e9;
color: #404040;
background-color: var(--color-sidebar-background);
color: var(--color-sidebar-link-text);
padding: 1px;
}
.version-list li:hover,
.version-list li a:focus {
background-color: #b9cfda;
background-color: var(--color-background-hover);
}
.version-list li.selected,
.version-list li.selected:hover {
background-color: #8d8c8c;
}
.version-list li.selected span {
cursor: default;
outline-color: red;
}
.version-arrow {
position: absolute;
width: 8px;
height: 8px;
left: 50%;
bottom: 4px;
margin-left: -4px;
transform: rotate(225deg);
background: #ede9e9;
border: 1px solid gray;
border-width: 1px 0 0 1px;
.version-list li.selected {
background: var(--color-sidebar-item-background--current);
font-weight: 700;
}

View File

@ -0,0 +1,6 @@
{%- extends "!base.html" -%}
{%- block theme_scripts -%}
{{ super() }}
<script defer data-domain="docs.blender.org" src="https://analytics.blender.org/js/script.js"></script>
{%- endblock -%}

View File

@ -1,6 +1,3 @@
{# For the "Report Issue" button on the bottom of pages. #}
{%- extends "!footer.html" %}
{%- block extrafooter %}
{%- if not pagename in ("search", "404", "genindex") and hasdoc(pagename) %}
<div class="footer-contribute">
<ul>
@ -13,10 +10,9 @@
#}Permanent+Link%5D%28https%3A%2F%2Fdocs.blender.org%2F{#
#}api%2F{{ version }}%2F{{ pagename }}{{ file_suffix }}%29%0D%0A%0D%0A%2A%2A{#
#}Short+description+of+error%2A%2A%0D%0A%5B{#
#}Please+fill+out+a+short+description+of+the+error+here%5D%0D%0A"
class="fa fa-bug"> {{ _('Report issue on this page') }}</a>
#}Please+fill+out+a+short+description+of+the+error+here%5D%0D%0A" class="fa fa-bug"> {{ _('Report issue
on this page') }}</a>
</li>
</ul>
</div>
{%- endif %}
{% endblock %}
{%- endif %}

View File

@ -0,0 +1,6 @@
{%- extends "!page.html" -%}
{%- block footer -%}
{{ super() }}
{%- include "components/footer_contribute.html" -%}
{%- endblock footer -%}

View File

@ -6,7 +6,6 @@
{{ release }}
</span>
<div class="version-dialog" aria-hidden="true">
<div class="version-arrow" aria-hidden="true"></div>
<div class="version-title">Versions</div>
<ul id="version-vsnlist" class="version-list" role="menu" aria-labelledby="version-popover" aria-hidden="true">
<li role="presentation">Loading...</li>
@ -27,4 +26,4 @@
</p>
</div>
</template>
</div>
</div>

View File

@ -119,7 +119,7 @@ BVHMetal::BVHMetal(const BVHParams &params_,
BVHMetal::~BVHMetal()
{
/* Clear point used by enqueuing. */
/* Clear point used by enqueueing. */
device->release_bvh(this);
if (@available(macos 12.0, *)) {

View File

@ -341,7 +341,7 @@ string MetalDevice::preprocess_source(MetalPipelineType pso_type,
}
# ifdef WITH_CYCLES_DEBUG
global_defines += "#define __KERNEL_DEBUG__\n";
global_defines += "#define WITH_CYCLES_DEBUG\n";
# endif
switch (device_vendor) {

View File

@ -144,7 +144,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg,
return false;
}
#if defined(__KERNEL_DEBUG__)
#if defined(WITH_CYCLES_DEBUG)
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
isect->t = ray->tmax;
isect->type = PRIMITIVE_NONE;
@ -281,7 +281,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
return false;
}
# if defined(__KERNEL_DEBUG__)
# if defined(WITH_CYCLES_DEBUG)
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
if (local_isect) {
local_isect->num_hits = 0;
@ -383,7 +383,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg,
return false;
}
# if defined(__KERNEL_DEBUG__)
# if defined(WITH_CYCLES_DEBUG)
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
kernel_assert(!"Invalid metal_ancillaries->accel_struct pointer");
return false;
@ -446,7 +446,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
return false;
}
# if defined(__KERNEL_DEBUG__)
# if defined(WITH_CYCLES_DEBUG)
if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) {
kernel_assert(!"Invalid metal_ancillaries->accel_struct pointer");
return false;

View File

@ -212,7 +212,7 @@ ccl_device_inline void surface_shader_prepare_closures(KernelGlobals kg,
/* BSDF */
#ifdef WITH_CYCLES_DEBUG
ccl_device_inline void surface_shader_validate_bsdf_sample(const KernelGlobals kg,
const ShaderClosure *sc,
ccl_private const ShaderClosure *sc,
const float3 wo,
const int org_label,
const float2 org_roughness,

View File

@ -319,14 +319,29 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
is_extension = module_name.startswith(_ext_base_pkg_idname_with_dot)
if handle_error is None:
def handle_error(_ex):
def handle_error(ex):
if isinstance(ex, ImportError):
# NOTE: checking "Add-on " prefix is rather weak,
# it's just a way to avoid the noise of a full trace-back when
# an add-on is simply missing on the file-system.
if (type(msg := ex.msg) is str) and msg.startswith("Add-on "):
print(msg)
return
import traceback
traceback.print_exc()
# reload if the mtime changes
mod = sys.modules.get(module_name)
# chances of the file _not_ existing are low, but it could be removed
if mod and os.path.exists(mod.__file__):
# Set to `mod.__file__` or None.
mod_file = None
if (
(mod is not None) and
(mod_file := mod.__file__) is not None and
os.path.exists(mod_file)
):
if getattr(mod, "__addon_enabled__", False):
# This is an unlikely situation,
@ -336,18 +351,15 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
try:
mod.unregister()
except BaseException as ex:
print(
"Exception in module unregister():",
repr(getattr(mod, "__file__", module_name)),
)
print("Exception in module unregister():", (mod_file or module_name))
handle_error(ex)
return None
mod.__addon_enabled__ = False
mtime_orig = getattr(mod, "__time__", 0)
mtime_new = os.path.getmtime(mod.__file__)
mtime_new = os.path.getmtime(mod_file)
if mtime_orig != mtime_new:
print("module changed on disk:", repr(mod.__file__), "reloading...")
print("module changed on disk:", repr(mod_file), "reloading...")
try:
importlib.reload(mod)
@ -374,11 +386,20 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
# Use instead of `__import__` so that sub-modules can eventually be supported.
# This is also documented to be the preferred way to import modules.
mod = importlib.import_module(module_name)
if mod.__file__ is None:
# This can happen when the addon has been removed but there are
# residual `.pyc` files left behind.
raise ImportError(name=module_name)
mod.__time__ = os.path.getmtime(mod.__file__)
if (mod_file := mod.__file__) is None:
# This can happen when:
# - The add-on has been removed but there are residual `.pyc` files left behind.
# - An extension is a directory that doesn't contain an `__init__.py` file.
#
# Include a message otherwise the "cause:" for failing to load the module is left blank.
# Include the `__path__` when available so there is a reference to the location that failed to load.
raise ImportError(
"module loaded with no associated file, __path__=%r, aborting!" % (
getattr(mod, "__path__", None)
),
name=module_name
)
mod.__time__ = os.path.getmtime(mod_file)
mod.__addon_enabled__ = False
except BaseException as ex:
# If the add-on doesn't exist, don't print full trace-back because the back-trace is in this case
@ -386,7 +407,7 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
# Account for `ImportError` & `ModuleNotFoundError`.
if isinstance(ex, ImportError):
if ex.name == module_name:
print("Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex)))
ex.msg = "Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex))
# Issue with an add-on from an extension repository, report a useful message.
elif is_extension and module_name.startswith(ex.name + "."):
@ -396,22 +417,20 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
None,
)
if repo is None:
print(
ex.msg = (
"Add-on not loaded: \"%s\", cause: extension repository \"%s\" doesn't exist" %
(module_name, repo_id)
)
elif not repo.enabled:
print(
ex.msg = (
"Add-on not loaded: \"%s\", cause: extension repository \"%s\" is disabled" %
(module_name, repo_id)
)
else:
# The repository exists and is enabled, it should have imported.
print("Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex)))
else:
handle_error(ex)
else:
handle_error(ex)
ex.msg = "Add-on not loaded: \"%s\", cause: %s" % (module_name, str(ex))
handle_error(ex)
if default_set:
_addon_remove(module_name)
@ -446,10 +465,7 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
try:
mod.register()
except BaseException as ex:
print(
"Exception in module register():",
getattr(mod, "__file__", module_name),
)
print("Exception in module register():", (mod_file or module_name))
handle_error(ex)
del sys.modules[module_name]
if default_set:

View File

@ -181,7 +181,7 @@ def write_sysinfo(filepath):
output.write("SDL: Blender was built without SDL support\n")
if bpy.app.background:
output.write("\nOpenGL: missing, background mode\n")
output.write("\nGPU: missing, background mode\n")
else:
output.write(title("GPU"))
output.write("renderer:\t%r\n" % gpu.platform.renderer_get())

View File

@ -443,9 +443,13 @@ class PREFERENCES_OT_addon_enable(Operator):
def err_cb(ex):
import traceback
traceback.print_exc()
# The full trace-back in the UI is unwieldy and associated with unhandled exceptions.
# Only show a single exception instead of the full trace-back,
# developers can debug using information printed in the console.
nonlocal err_str
err_str = traceback.format_exc()
print(err_str)
err_str = str(ex)
mod = addon_utils.enable(self.module, default_set=True, handle_error=err_cb)

View File

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

View File

@ -155,6 +155,7 @@ class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
self.operator_modifier_add(layout, 'WIREFRAME')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_ARRAY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_BUILD')
self.operator_modifier_add(layout, 'GREASE_PENCIL_DASH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_ENVELOPE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_LENGTH')

View File

@ -727,6 +727,9 @@ class RENDER_PT_eevee_next_shadows(RenderButtonsPanel, Panel):
col = layout.column()
col.prop(props, "shadow_normal_bias", text="Normal Bias")
col = layout.column()
col.prop(props, "use_shadow_jittered_viewport", text="Jittered Transparency (Viewport)")
class RENDER_PT_eevee_sampling(RenderButtonsPanel, Panel):
bl_label = "Sampling"

View File

@ -2251,8 +2251,11 @@ class USERPREF_PT_addons(AddOnPanel, Panel):
prefs = context.preferences
use_extension_repos = prefs.experimental.use_extension_repos
if use_extension_repos and self.is_extended():
if (
prefs.view.show_developer_ui and
prefs.experimental.use_extension_repos and
self.is_extended()
):
# Rely on the draw function being appended to by the extensions add-on.
return

View File

@ -2739,7 +2739,7 @@ class VIEW3D_MT_image_add(Menu):
def draw(self, _context):
layout = self.layout
# Expliclitly set background mode on/off as operator will try to
# Explicitly set background mode on/off as operator will try to
# auto detect which mode to use otherwise.
layout.operator("object.empty_image_add", text="Reference", icon='IMAGE_REFERENCE').background = False
layout.operator("object.empty_image_add", text="Background", icon='IMAGE_BACKGROUND').background = True

View File

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

View File

@ -110,6 +110,7 @@ enum eCbEvent {
BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST,
BKE_CB_EVT_EXTENSION_REPOS_SYNC,
BKE_CB_EVT_EXTENSION_REPOS_UPGRADE,
BKE_CB_EVT_EXTENSION_REPOS_FILES_CLEAR,
BKE_CB_EVT_TOT,
};

View File

@ -86,11 +86,11 @@ char BKE_imtype_valid_depths(char imtype);
/**
* String is from command line `--render-format` argument,
* keep in sync with `creator_args.c` help info.
* keep in sync with `creator_args.cc` help info.
*/
char BKE_imtype_from_arg(const char *imtype_arg);
/* Conversion between ImBuf settings. */
/* Conversion between #ImBuf settings. */
void BKE_image_format_from_imbuf(struct ImageFormatData *im_format, const struct ImBuf *imbuf);
void BKE_image_format_to_imbuf(struct ImBuf *ibuf, const struct ImageFormatData *imf);

View File

@ -112,7 +112,7 @@ void BKE_scene_object_base_flag_sync_from_base(Base *base);
*/
void BKE_scene_set_background(Main *bmain, Scene *sce);
/**
* Called from `creator_args.c`.
* Called from `creator_args.cc`.
*/
Scene *BKE_scene_set_name(Main *bmain, const char *name);

View File

@ -1737,6 +1737,97 @@ static void legacy_object_modifier_weight_lineart(Object &object, GpencilModifie
greasepencil::convert::lineart_wrap_v3(&legacy_md_lineart, &md_lineart);
}
static void legacy_object_modifier_build(Object &object, GpencilModifierData &legacy_md)
{
ModifierData &md = legacy_object_modifier_common(
object, eModifierType_GreasePencilBuild, legacy_md);
auto &md_build = reinterpret_cast<GreasePencilBuildModifierData &>(md);
auto &legacy_md_build = reinterpret_cast<BuildGpencilModifierData &>(legacy_md);
md_build.flag = 0;
if (legacy_md_build.flag & GP_BUILD_RESTRICT_TIME) {
md_build.flag |= MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME;
}
if (legacy_md_build.flag & GP_BUILD_USE_FADING) {
md_build.flag |= MOD_GREASE_PENCIL_BUILD_USE_FADING;
}
switch (legacy_md_build.mode) {
case GP_BUILD_MODE_ADDITIVE:
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE;
break;
case GP_BUILD_MODE_CONCURRENT:
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT;
break;
case GP_BUILD_MODE_SEQUENTIAL:
default:
md_build.mode = MOD_GREASE_PENCIL_BUILD_MODE_SEQUENTIAL;
break;
}
switch (legacy_md_build.time_alignment) {
default:
case GP_BUILD_TIMEALIGN_START:
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEALIGN_START;
break;
case GP_BUILD_TIMEALIGN_END:
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEALIGN_END;
break;
}
switch (legacy_md_build.time_mode) {
default:
case GP_BUILD_TIMEMODE_FRAMES:
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES;
break;
case GP_BUILD_TIMEMODE_PERCENTAGE:
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE;
break;
case GP_BUILD_TIMEMODE_DRAWSPEED:
md_build.mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED;
break;
}
switch (legacy_md_build.transition) {
default:
case GP_BUILD_TRANSITION_GROW:
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW;
break;
case GP_BUILD_TRANSITION_SHRINK:
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_SHRINK;
break;
case GP_BUILD_TRANSITION_VANISH:
md_build.mode = MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
break;
}
md_build.start_frame = legacy_md_build.start_frame;
md_build.end_frame = legacy_md_build.end_frame;
md_build.start_delay = legacy_md_build.start_delay;
md_build.length = legacy_md_build.length;
md_build.fade_fac = legacy_md_build.fade_fac;
md_build.fade_opacity_strength = legacy_md_build.fade_opacity_strength;
md_build.fade_thickness_strength = legacy_md_build.fade_thickness_strength;
md_build.percentage_fac = legacy_md_build.percentage_fac;
md_build.speed_fac = legacy_md_build.speed_fac;
md_build.speed_maxgap = legacy_md_build.speed_maxgap;
STRNCPY(md_build.target_vgname, legacy_md_build.target_vgname);
legacy_object_modifier_influence(md_build.influence,
legacy_md_build.layername,
legacy_md_build.layer_pass,
legacy_md_build.flag & GP_WEIGHT_INVERT_LAYER,
legacy_md_build.flag & GP_WEIGHT_INVERT_LAYERPASS,
&legacy_md_build.material,
legacy_md_build.pass_index,
legacy_md_build.flag & GP_WEIGHT_INVERT_MATERIAL,
legacy_md_build.flag & GP_WEIGHT_INVERT_PASS,
legacy_md_build.target_vgname,
legacy_md_build.flag & GP_WEIGHT_INVERT_VGROUP,
nullptr,
false);
}
static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
{
BLI_assert(BLI_listbase_is_empty(&object.modifiers));
@ -1818,6 +1909,8 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
legacy_object_modifier_weight_lineart(object, *gpd_md);
break;
case eGpencilModifierType_Build:
legacy_object_modifier_build(object, *gpd_md);
break;
case eGpencilModifierType_Simplify:
case eGpencilModifierType_Texture:
break;

View File

@ -298,7 +298,7 @@ void BKE_lattice_resize(Lattice *lt, int uNew, int vNew, int wNew, Object *ltOb)
calc_lat_fudu(lt->flag, wNew, &fw, &dw);
/* If old size is different than resolution changed in interface,
* try to do clever reinit of points. Pretty simply idea, we just
* try to do clever reinitialize of points. Pretty simply idea, we just
* deform new verts by old lattice, but scaling them to match old
* size first.
*/

View File

@ -2127,11 +2127,10 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
}
group->geometry_node_asset_traits->flag |= GEO_NODE_ASSET_MODIFIER;
group->tree_interface.add_socket(DATA_("Geometry"),
"",
"NodeSocketGeometry",
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
nullptr);
group->tree_interface.add_socket(
DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
group->tree_interface.add_socket(
DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
bNodeTreeInterfaceSocket *angle_io_socket = group->tree_interface.add_socket(
DATA_("Angle"), "", "NodeSocketFloat", NODE_INTERFACE_SOCKET_INPUT, nullptr);
auto &angle_data = *static_cast<bNodeSocketValueFloat *>(angle_io_socket->socket_data);
@ -2146,7 +2145,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
group_input_angle->locx = -420.0f;
group_input_angle->locy = -300.0f;
LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_angle->outputs) {
if (!STREQ(socket->identifier, "Socket_1")) {
if (!STREQ(socket->identifier, "Socket_2")) {
socket->flag |= SOCK_HIDDEN;
}
}
@ -2154,7 +2153,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
group_input_mesh->locx = -60.0f;
group_input_mesh->locy = -100.0f;
LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_mesh->outputs) {
if (!STREQ(socket->identifier, "Socket_0")) {
if (!STREQ(socket->identifier, "Socket_1")) {
socket->flag |= SOCK_HIDDEN;
}
}
@ -2197,7 +2196,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
nodeFindSocket(group_output, SOCK_IN, "Socket_0"));
nodeAddLink(group,
group_input_angle,
nodeFindSocket(group_input_angle, SOCK_OUT, "Socket_1"),
nodeFindSocket(group_input_angle, SOCK_OUT, "Socket_2"),
less_than_or_equal,
nodeFindSocket(less_than_or_equal, SOCK_IN, "B"));
nodeAddLink(group,
@ -2212,7 +2211,7 @@ static bNodeTree *add_auto_smooth_node_tree(Main &bmain)
nodeFindSocket(boolean_and, SOCK_IN, "Boolean_001"));
nodeAddLink(group,
group_input_mesh,
nodeFindSocket(group_input_mesh, SOCK_OUT, "Socket_0"),
nodeFindSocket(group_input_mesh, SOCK_OUT, "Socket_1"),
shade_smooth_edge,
nodeFindSocket(shade_smooth_edge, SOCK_IN, "Geometry"));
nodeAddLink(group,
@ -2336,14 +2335,14 @@ static ModifierData *create_auto_smooth_modifier(
id_us_plus(&md->node_group->id);
md->settings.properties = idprop::create_group("Nodes Modifier Settings").release();
IDProperty *angle_prop = idprop::create("Socket_1", angle).release();
IDProperty *angle_prop = idprop::create("Socket_2", angle).release();
auto *ui_data = reinterpret_cast<IDPropertyUIDataFloat *>(IDP_ui_data_ensure(angle_prop));
ui_data->base.rna_subtype = PROP_ANGLE;
ui_data->soft_min = 0.0f;
ui_data->soft_max = DEG2RADF(180.0f);
IDP_AddToGroup(md->settings.properties, angle_prop);
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_1_use_attribute", 0).release());
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_1_attribute_name", "").release());
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_use_attribute", 0).release());
IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_attribute_name", "").release());
BKE_modifiers_persistent_uid_init(object, md->modifier);
return &md->modifier;

View File

@ -1190,6 +1190,10 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(const blender::StringRe
const NodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent)
{
/* Check that each interface socket is either an input or an output. Technically, it can be both
* at the same time, but we don't want that for the time being. */
BLI_assert(((NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT) & flag) !=
(NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT));
if (parent == nullptr) {
parent = &root_panel;
}

View File

@ -92,8 +92,19 @@ int BLI_rename(const char *from, const char *to) ATTR_NONNULL();
*/
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL();
/**
* Deletes the specified file or directory (depending on dir), optionally
* doing recursive delete of directory contents.
* Deletes the specified file or directory.
*
* \param dir: Delete an empty directory instead of a file.
* The value is ignored when `recursive` is true but should true to make the intention clear.
* If the directory is not empty, delete fails.
* \param recursive: Recursively delete files including `path` which may be a directory of a file.
*
* \note Symbolic-Links for (UNIX) behave as follows:
* - Never followed, treated as regular files.
* - Links are removed, not the files/directories they references.
* - When `path` itself links to another directory,
* deleting `path` behaves as if a regular file is being deleted.
* - If `dir` is true and `path` is a link, delete fails.
*
* \return zero on success (matching 'remove' behavior).
*/

View File

@ -250,7 +250,7 @@ template<typename Function> inline void isolate_task(const Function &function)
/**
* Should surround parallel code that is highly bandwidth intensive, e.g. it just fills a buffer
* with no or just few additional operations. If the buffers are large, it's benefitial to limit
* with no or just few additional operations. If the buffers are large, it's beneficial to limit
* the number of threads doing the work because that just creates more overhead on the hardware
* level and doesn't provide a notable performance benefit beyond a certain point.
*/
@ -261,7 +261,7 @@ inline void memory_bandwidth_bound_task(const int64_t approximate_bytes_touched,
/* Don't limit threading when all touched memory can stay in the CPU cache, because there a much
* higher memory bandwidth is available compared to accessing RAM. This value is supposed to be
* on the order of the L3 cache size. Accessing that value is not quite straight forward and even
* if it was, it's not clear if using the exact cache size would be benefitial because there is
* if it was, it's not clear if using the exact cache size would be beneficial because there is
* often more stuff going on on the CPU at the same time. */
if (approximate_bytes_touched <= 8 * 1024 * 1024) {
function();

View File

@ -46,7 +46,7 @@
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_string_utils.hh"
#include "BLI_sys_types.h" /* for intptr_t support */
#include "BLI_sys_types.h" /* For `intptr_t` support. */
#include "BLI_utildefines.h"
/** Sizes above this must be allocated. */
@ -292,19 +292,19 @@ bool BLI_file_is_writable(const char *filepath)
{
bool writable;
if (BLI_access(filepath, W_OK) == 0) {
/* file exists and I can write to it */
/* File exists and I can write to it. */
writable = true;
}
else if (errno != ENOENT) {
/* most likely file or containing directory cannot be accessed */
/* Most likely file or containing directory cannot be accessed. */
writable = false;
}
else {
/* file doesn't exist -- check I can create it in parent directory */
/* File doesn't exist -- check I can create it in parent directory. */
char parent[FILE_MAX];
BLI_path_split_dir_part(filepath, parent, sizeof(parent));
#ifdef WIN32
/* windows does not have X_OK */
/* Windows does not have X_OK. */
writable = BLI_access(parent, W_OK) == 0;
#else
writable = BLI_access(parent, X_OK | W_OK) == 0;
@ -321,7 +321,7 @@ bool BLI_file_touch(const char *filepath)
int c = getc(f);
if (c == EOF) {
/* Empty file, reopen in truncate write mode... */
/* Empty file, reopen in truncate write mode. */
fclose(f);
f = BLI_fopen(filepath, "w+b");
}
@ -734,6 +734,9 @@ int BLI_delete(const char *path, bool dir, bool recursive)
BLI_assert(!BLI_path_is_rel(path));
/* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
BLI_assert(!(dir == false && recursive == true));
if (recursive) {
err = delete_recursive(path);
}
@ -1089,6 +1092,9 @@ static int recursive_operation_impl(StrBuf *src_buf,
* prefixing it with path_dst, recursively scanning subdirectories, and invoking the specified
* callbacks for files and subdirectories found as appropriate.
*
* \note Symbolic links are *not* followed, even when `path_src` links to a directory,
* it wont be recursed down. Support for this could be added.
*
* \param path_src: Top-level source path.
* \param path_dst: Top-level destination path.
* \param callback_dir_pre: Optional, to be invoked before entering a subdirectory,
@ -1222,7 +1228,7 @@ static int delete_soft(const char *file, const char **error_message)
int pid = fork();
if (pid != 0) {
/* Parent process */
/* Parent process. */
int wstatus = 0;
waitpid(pid, &wstatus, 0);
@ -1243,7 +1249,7 @@ static int delete_soft(const char *file, const char **error_message)
execvp(args[0], (char **)args);
*error_message = "Forking process failed.";
return -1; /* This should only be reached if execvp fails and stack isn't replaced. */
return -1; /* This should only be reached if `execvp` fails and stack isn't replaced. */
}
# endif
@ -1278,6 +1284,8 @@ int BLI_access(const char *filepath, int mode)
int BLI_delete(const char *path, bool dir, bool recursive)
{
BLI_assert(!BLI_path_is_rel(path));
/* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
BLI_assert(!(dir == false && recursive == true));
if (recursive) {
return recursive_operation(path, nullptr, nullptr, delete_single_file, delete_callback_post);
@ -1347,13 +1355,13 @@ static int copy_callback_pre(const char *from, const char *to)
return RecursiveOp_Callback_Error;
}
/* create a directory */
/* Create a directory. */
if (mkdir(to, st.st_mode)) {
perror("mkdir");
return RecursiveOp_Callback_Error;
}
/* set proper owner and group on new directory */
/* Set proper owner and group on new directory. */
if (chown(to, st.st_uid, st.st_gid)) {
perror("chown");
return RecursiveOp_Callback_Error;
@ -1380,12 +1388,12 @@ static int copy_single_file(const char *from, const char *to)
}
if (S_ISLNK(st.st_mode)) {
/* symbolic links should be copied in special way */
/* Symbolic links should be copied in special way. */
char *link_buffer;
int need_free;
int64_t link_len;
/* get large enough buffer to read link content */
/* Get large enough buffer to read link content. */
if ((st.st_size + 1) < sizeof(buf)) {
link_buffer = buf;
need_free = 0;
@ -1423,7 +1431,7 @@ static int copy_single_file(const char *from, const char *to)
return RecursiveOp_Callback_OK;
}
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
/* copy special type of file */
/* Copy special type of file. */
if (mknod(to, st.st_mode, st.st_rdev)) {
perror("mknod");
return RecursiveOp_Callback_Error;

View File

@ -467,10 +467,11 @@ static void evaluate_coarse_difference(const Span<DifferenceCourseBoundary> boun
* #CoarseSegment::Type::Unknown. Those segments can be evaluated in more detail afterwards.
*
* \param root_expression: Expression to be evaluated.
* \param eval_order: Pre-computed evaluation order. All children of a term must come before
* the term itself.
* \param eval_bounds: If given, the evaluation is restriced to those bounds. Otherwise, the full
* referenced masks are used.
* \param eval_order: Pre-computed evaluation order.
* All children of a term must come before the term itself.
* \param eval_bounds: If given, the evaluation is restricted to those bounds.
* Otherwise, the full
* referenced masks are used.
*/
static CoarseResult evaluate_coarse(const Expr &root_expression,
const Span<const Expr *> eval_order,
@ -1148,7 +1149,7 @@ static void evaluate_short_unknown_segments_exactly(
}
case ExactEvalMode::Indices: {
/* #evaluate_exact_with_indices requires that all index masks have a single segment in the
* provided bounds. So split up the range into subranges first if necessary. */
* provided bounds. So split up the range into sub-ranges first if necessary. */
Vector<int64_t, 16> split_indices;
/* Always adding the beginning and end of the bounds simplifies the code below. */
split_indices.extend({bounds.first(), bounds.one_after_last()});

View File

@ -4800,8 +4800,9 @@ void lookat_m4(
i_multmatrix(mat1, mat);
mat1[1][1] = mat1[2][2] = 1.0f; /* be careful here to reinit */
mat1[1][2] = mat1[2][1] = 0.0f; /* those modified by the last */
/* Be careful here to reinitialize those modified by the last. */
mat1[1][1] = mat1[2][2] = 1.0f;
mat1[1][2] = mat1[2][1] = 0.0f;
/* paragraph */
if (hyp != 0.0f) { /* rotate Y */

View File

@ -228,7 +228,7 @@ void memory_bandwidth_bound_task_impl(const FunctionRef<void()> function)
#ifdef WITH_TBB
/* This is the maximum number of threads that may perform these memory bandwidth bound tasks at
* the same time. Often fewer threads are already enough to use up the full bandwidth capacity.
* Additional threads usually have a negilible benefit and can even make performance worse.
* Additional threads usually have a negligible benefit and can even make performance worse.
*
* It's better to use fewer threads here so that the CPU cores can do other tasks at the same
* time which may be more compute intensive. */

View File

@ -314,7 +314,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb)
case SPACE_ACTION: {
SpaceAction *saction = (SpaceAction *)sl;
/* We totally reinit the view for the Action Editor,
/* We totally reinitialize the view for the Action Editor,
* as some old instances had some weird cruft set. */
region->v2d.tot.xmin = -20.0f;
region->v2d.tot.ymin = float(-area->winy) / 3.0f;

View File

@ -554,11 +554,10 @@ static bNodeTree *add_realize_node_tree(Main *bmain)
{
bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree");
node_tree->tree_interface.add_socket("Geometry",
"",
"NodeSocketGeometry",
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
nullptr);
node_tree->tree_interface.add_socket(
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
node_tree->tree_interface.add_socket(
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
bNode *group_input = nodeAddStaticNode(nullptr, node_tree, NODE_GROUP_INPUT);
group_input->locx = -400.0f;

View File

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

View File

@ -299,7 +299,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
graph_id_tag_update(deg_graph->bmain,
deg_graph,
&deg_graph->scene->id,
ID_RECALC_BASE_FLAGS,
ID_RECALC_BASE_FLAGS | ID_RECALC_HIERARCHY,
deg::DEG_UPDATE_SOURCE_RELATIONS);
}
}

View File

@ -80,6 +80,15 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
this->pcf_radius = la->shadow_filter_radius;
float resolution_scale = std::clamp(la->shadow_resolution_scale, 0.0f, 2.0f);
if (resolution_scale < 1.0) {
this->lod_bias = -log2(resolution_scale);
}
else {
this->lod_bias = -(resolution_scale - 1.0f);
}
this->lod_bias += shadows.get_global_lod_bias();
eLightType new_type = to_light_type(la->type, la->area_shape, la->mode & LA_USE_SOFT_FALLOFF);
if (assign_if_different(this->type, new_type)) {
shadow_discard_safe(shadows);

View File

@ -356,6 +356,10 @@ Material &MaterialModule::material_sync(Object *ob,
mat.is_alpha_blend_transparent = use_forward_pipeline &&
GPU_material_flag_get(mat.shading.gpumat,
GPU_MATFLAG_TRANSPARENT);
mat.has_transparent_shadows = blender_mat->blend_flag & MA_BL_TRANSPARENT_SHADOW &&
GPU_material_flag_get(mat.shading.gpumat,
GPU_MATFLAG_TRANSPARENT);
return mat;
});

View File

@ -291,6 +291,7 @@ struct MaterialPass {
struct Material {
bool is_alpha_blend_transparent;
bool has_transparent_shadows;
bool has_surface;
bool has_volume;
MaterialPass shadow;

View File

@ -797,8 +797,6 @@ struct LightData {
float radius_squared;
/** Spot angle tangent. */
float spot_tan;
/** Reuse for directional LOD bias. */
#define _clipmap_lod_bias spot_tan
/** --- Shadow Data --- */
/** Near clip distances. Float stored as int for atomic operations. */
@ -821,7 +819,8 @@ struct LightData {
float shadow_trace_distance;
/* Radius in pixels for shadow filtering. */
float pcf_radius;
int _pad0;
/* Shadow Map resolution bias. */
float lod_bias;
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
@ -1028,7 +1027,8 @@ struct ShadowSceneData {
int step_count;
/* Bias the shading point by using the normal to avoid self intersection. */
float normal_bias;
int _pad0;
/* Ratio between tile-map pixel world "radius" and film pixel world "radius". */
float tilemap_projection_ratio;
};
BLI_STATIC_ASSERT_ALIGN(ShadowSceneData, 16)

View File

@ -531,7 +531,7 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera
/* The bias is applied in cascade_level_range().
* Using clipmap_lod_min here simplify code in shadow_directional_level().
* Minus 1 because of the ceil(). */
light._clipmap_lod_bias = light.clipmap_lod_min - 1;
light.lod_bias = light.clipmap_lod_min - 1;
}
/************************************************************************
@ -621,7 +621,7 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light,
light.clipmap_lod_min = levels_range.first();
light.clipmap_lod_max = levels_range.last();
light._clipmap_lod_bias = lod_bias;
light.lod_bias = lod_bias;
}
void ShadowDirectional::sync(const float4x4 &object_mat,
@ -742,6 +742,9 @@ void ShadowModule::init()
}
}
jittered_transparency_ = !inst_.is_viewport() ||
scene.eevee.flag & SCE_EEVEE_SHADOW_JITTERED_VIEWPORT;
data_.ray_count = clamp_i(inst_.scene->eevee.shadow_ray_count, 1, SHADOW_MAX_RAY);
data_.step_count = clamp_i(inst_.scene->eevee.shadow_step_count, 1, SHADOW_MAX_STEP);
data_.normal_bias = max_ff(inst_.scene->eevee.shadow_normal_bias, 0.0f);
@ -757,7 +760,7 @@ void ShadowModule::init()
simplify_shadows = inst_.is_viewport() ? scene.r.simplify_shadows :
scene.r.simplify_shadows_render;
}
lod_bias_ = math::interpolate(float(SHADOW_TILEMAP_LOD), 0.0f, simplify_shadows);
lod_bias_ = -log2(simplify_shadows);
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);
@ -824,6 +827,8 @@ void ShadowModule::begin_sync()
past_casters_updated_.clear();
curr_casters_updated_.clear();
curr_casters_.clear();
jittered_transparent_casters_.clear();
update_casters_ = true;
{
Manager &manager = *inst_.manager;
@ -849,7 +854,7 @@ void ShadowModule::begin_sync()
sub.bind_ssbo("surfel_buf", &surfels_buf);
sub.bind_ssbo("capture_info_buf", &capture_info_buf);
sub.push_constant("directional_level", directional_level);
sub.push_constant("tilemap_projection_ratio", projection_ratio);
sub.push_constant("tilemap_proj_ratio", projection_ratio);
sub.bind_resources(inst_.lights);
sub.dispatch(&inst_.volume_probes.bake.dispatch_per_surfel_);
@ -864,7 +869,7 @@ void ShadowModule::begin_sync()
sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data);
sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data);
sub.bind_texture("depth_tx", &src_depth_tx_);
sub.push_constant("tilemap_projection_ratio", &tilemap_projection_ratio_);
sub.push_constant("tilemap_proj_ratio", &data_.tilemap_projection_ratio);
sub.bind_resources(inst_.lights);
sub.dispatch(&dispatch_depth_scan_size_);
}
@ -880,7 +885,7 @@ void ShadowModule::begin_sync()
sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data);
sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data);
sub.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
sub.push_constant("tilemap_projection_ratio", &tilemap_projection_ratio_);
sub.push_constant("tilemap_proj_ratio", &data_.tilemap_projection_ratio);
sub.push_constant("pixel_world_radius", &pixel_world_radius_);
sub.push_constant("fb_resolution", &usage_tag_fb_resolution_);
sub.push_constant("fb_lod", &usage_tag_fb_lod_);
@ -897,7 +902,8 @@ void ShadowModule::begin_sync()
void ShadowModule::sync_object(const Object *ob,
const ObjectHandle &handle,
const ResourceHandle &resource_handle,
bool is_alpha_blend)
bool is_alpha_blend,
bool has_transparent_shadows)
{
bool is_shadow_caster = !(ob->visibility_flag & OB_HIDE_SHADOW);
if (!is_shadow_caster && !is_alpha_blend) {
@ -907,11 +913,18 @@ void ShadowModule::sync_object(const Object *ob,
ShadowObject &shadow_ob = objects_.lookup_or_add_default(handle.object_key);
shadow_ob.used = true;
const bool is_initialized = shadow_ob.resource_handle.raw != 0;
if ((handle.recalc != 0 || !is_initialized) && is_shadow_caster) {
if (shadow_ob.resource_handle.raw != 0) {
const bool has_jittered_transparency = has_transparent_shadows && jittered_transparency_;
if (is_shadow_caster && (handle.recalc || !is_initialized || has_jittered_transparency)) {
if (handle.recalc && is_initialized) {
past_casters_updated_.append(shadow_ob.resource_handle.raw);
}
curr_casters_updated_.append(resource_handle.raw);
if (has_jittered_transparency) {
jittered_transparent_casters_.append(resource_handle.raw);
}
else {
curr_casters_updated_.append(resource_handle.raw);
}
}
shadow_ob.resource_handle = resource_handle;
@ -932,7 +945,7 @@ void ShadowModule::end_sync()
light.shadow_discard_safe(*this);
}
else if (light.directional != nullptr) {
light.directional->release_excess_tilemaps(inst_.camera, lod_bias_);
light.directional->release_excess_tilemaps(inst_.camera, light.lod_bias);
}
else if (light.punctual != nullptr) {
light.punctual->release_excess_tilemaps();
@ -946,10 +959,10 @@ void ShadowModule::end_sync()
light.tilemap_index = LIGHT_NO_SHADOW;
}
else if (light.directional != nullptr) {
light.directional->end_sync(light, inst_.camera, lod_bias_);
light.directional->end_sync(light, inst_.camera, light.lod_bias);
}
else if (light.punctual != nullptr) {
light.punctual->end_sync(light, lod_bias_);
light.punctual->end_sync(light, light.lod_bias);
}
else {
light.tilemap_index = LIGHT_NO_SHADOW;
@ -973,6 +986,7 @@ void ShadowModule::end_sync()
}
past_casters_updated_.push_update();
curr_casters_updated_.push_update();
jittered_transparent_casters_.push_update();
curr_casters_.push_update();
@ -1080,6 +1094,22 @@ void ShadowModule::end_sync()
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
}
{
/* Mark for update all shadow pages touching a jittered transparency shadow caster. */
PassSimple &pass = jittered_transparent_caster_update_ps_;
pass.init();
if (jittered_transparent_casters_.size() > 0) {
pass.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_UPDATE));
pass.bind_ssbo("tilemaps_buf", tilemap_pool.tilemaps_data);
pass.bind_ssbo("tiles_buf", tilemap_pool.tiles_data);
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
pass.bind_ssbo("resource_ids_buf", jittered_transparent_casters_);
pass.dispatch(
int3(jittered_transparent_casters_.size(), 1, tilemap_pool.tilemaps_data.size()));
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
}
}
/* Non volume usage tagging happens between these two steps.
* (Setup at begin_sync) */
@ -1088,7 +1118,7 @@ void ShadowModule::end_sync()
sub.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_TAG_USAGE_VOLUME));
sub.bind_ssbo("tilemaps_buf", &tilemap_pool.tilemaps_data);
sub.bind_ssbo("tiles_buf", &tilemap_pool.tiles_data);
sub.push_constant("tilemap_projection_ratio", &tilemap_projection_ratio_);
sub.push_constant("tilemap_proj_ratio", &data_.tilemap_projection_ratio);
sub.bind_resources(inst_.uniform_data);
sub.bind_resources(inst_.hiz_buffer.front);
sub.bind_resources(inst_.sampling);
@ -1310,7 +1340,8 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
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));
tilemap_projection_ratio_ = tilemap_pixel_radius() / pixel_world_radius_;
data_.tilemap_projection_ratio = tilemap_pixel_radius() / pixel_world_radius_;
inst_.uniform_data.push_update();
usage_tag_fb_resolution_ = math::divide_ceil(int2(target_size),
int2(std::exp2(usage_tag_fb_lod_)));
@ -1338,7 +1369,7 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
}
inst_.hiz_buffer.update();
bool update_casters = true;
bool first_loop = true;
do {
DRW_stats_group_start("Shadow");
@ -1346,12 +1377,15 @@ void ShadowModule::set_view(View &view, GPUTexture *depth_tx)
GPU_uniformbuf_clear_to_zero(shadow_multi_view_.matrices_ubo_get());
inst_.manager->submit(tilemap_setup_ps_, view);
if (assign_if_different(update_casters, false)) {
if (assign_if_different(update_casters_, false)) {
/* Run caster update only once. */
/* TODO(fclem): There is an optimization opportunity here where we can
* test casters only against the static tilemaps instead of all of them. */
inst_.manager->submit(caster_update_ps_, view);
}
if (assign_if_different(first_loop, false)) {
inst_.manager->submit(jittered_transparent_caster_update_ps_, view);
}
inst_.manager->submit(tilemap_usage_ps_, view);
inst_.manager->submit(tilemap_update_ps_, view);

View File

@ -213,6 +213,11 @@ class ShadowModule {
/** Map of shadow casters to track deletion & update of intersected shadows. */
Map<ObjectKey, ShadowObject> objects_;
/* Used to call caster_update_ps_ only once per sync (Initialized on begin_sync). */
bool update_casters_ = false;
bool jittered_transparency_ = false;
/* -------------------------------------------------------------------- */
/** \name Tile-map Management
* \{ */
@ -229,9 +234,11 @@ class ShadowModule {
Framebuffer usage_tag_fb;
PassSimple caster_update_ps_ = {"CasterUpdate"};
PassSimple jittered_transparent_caster_update_ps_ = {"TransparentCasterUpdate"};
/** List of Resource IDs (to get bounds) for tagging passes. */
StorageVectorBuffer<uint, 128> past_casters_updated_ = {"PastCastersUpdated"};
StorageVectorBuffer<uint, 128> curr_casters_updated_ = {"CurrCastersUpdated"};
StorageVectorBuffer<uint, 128> jittered_transparent_casters_ = {"JitteredTransparentCasters"};
/** List of Resource IDs (to get bounds) for getting minimum clip-maps bounds. */
StorageVectorBuffer<uint, 128> curr_casters_ = {"CurrCasters"};
@ -249,8 +256,6 @@ class ShadowModule {
StorageArrayBuffer<uint, SHADOW_VIEW_MAX, true> viewport_index_buf_ = {"viewport_index_buf"};
int3 dispatch_depth_scan_size_;
/* Ratio between tile-map pixel world "radius" and film pixel world "radius". */
float tilemap_projection_ratio_;
float pixel_world_radius_;
int2 usage_tag_fb_resolution_;
int usage_tag_fb_lod_ = 5;
@ -334,7 +339,8 @@ class ShadowModule {
void sync_object(const Object *ob,
const ObjectHandle &handle,
const ResourceHandle &resource_handle,
bool is_alpha_blend);
bool is_alpha_blend,
bool has_transparent_shadows);
void end_sync();
void set_lights_data();
@ -359,6 +365,11 @@ class ShadowModule {
return data_;
}
float get_global_lod_bias()
{
return lod_bias_;
}
private:
void remove_unused();
void debug_page_map_call(DRWPass *pass);

View File

@ -114,6 +114,7 @@ void SyncModule::sync_mesh(Object *ob,
}
bool is_alpha_blend = false;
bool has_transparent_shadows = false;
float inflate_bounds = 0.0f;
for (auto i : material_array.gpu_materials.index_range()) {
GPUBatch *geom = mat_geom[i];
@ -147,6 +148,7 @@ void SyncModule::sync_mesh(Object *ob,
geometry_call(material.reflection_probe_shading.sub_pass, geom, res_handle);
is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent;
has_transparent_shadows = has_transparent_shadows || material.has_transparent_shadows;
::Material *mat = GPU_material_get_material(gpu_material);
inst_.cryptomatte.sync_material(mat);
@ -162,7 +164,7 @@ void SyncModule::sync_mesh(Object *ob,
inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials);
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows);
inst_.cryptomatte.sync_object(ob, res_handle);
}
@ -192,6 +194,7 @@ bool SyncModule::sync_sculpt(Object *ob,
MaterialArray &material_array = inst_.materials.material_array_get(ob, has_motion);
bool is_alpha_blend = false;
bool has_transparent_shadows = false;
float inflate_bounds = 0.0f;
for (SculptBatch &batch :
sculpt_batches_per_material_get(ob_ref.object, material_array.gpu_materials))
@ -226,6 +229,7 @@ bool SyncModule::sync_sculpt(Object *ob,
geometry_call(material.reflection_probe_shading.sub_pass, geom, res_handle);
is_alpha_blend = is_alpha_blend || material.is_alpha_blend_transparent;
has_transparent_shadows = has_transparent_shadows || material.has_transparent_shadows;
GPUMaterial *gpu_material = material_array.gpu_materials[batch.material_slot];
::Material *mat = GPU_material_get_material(gpu_material);
@ -245,7 +249,7 @@ bool SyncModule::sync_sculpt(Object *ob,
inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials);
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows);
inst_.cryptomatte.sync_object(ob, res_handle);
return true;
@ -308,13 +312,15 @@ void SyncModule::sync_point_cloud(Object *ob,
::Material *mat = GPU_material_get_material(gpu_material);
inst_.cryptomatte.sync_material(mat);
bool is_alpha_blend = material.is_alpha_blend_transparent;
if (GPU_material_has_displacement_output(gpu_material) && mat->inflate_bounds != 0.0f) {
inst_.manager->update_handle_bounds(res_handle, ob_ref, mat->inflate_bounds);
}
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
inst_.shadows.sync_object(ob,
ob_handle,
res_handle,
material.is_alpha_blend_transparent,
material.has_transparent_shadows);
}
/** \} */
@ -485,8 +491,9 @@ void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandl
gpencil_drawcall_flush(iter);
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
bool has_transparent_shadows = true; /* TODO material.has_transparent_shadows. */
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend, has_transparent_shadows);
}
/** \} */
@ -560,13 +567,15 @@ void SyncModule::sync_curves(Object *ob,
::Material *mat = GPU_material_get_material(gpu_material);
inst_.cryptomatte.sync_material(mat);
bool is_alpha_blend = material.is_alpha_blend_transparent;
if (GPU_material_has_displacement_output(gpu_material) && mat->inflate_bounds != 0.0f) {
inst_.manager->update_handle_bounds(res_handle, ob_ref, mat->inflate_bounds);
}
inst_.shadows.sync_object(ob, ob_handle, res_handle, is_alpha_blend);
inst_.shadows.sync_object(ob,
ob_handle,
res_handle,
material.is_alpha_blend_transparent,
material.has_transparent_shadows);
}
/** \} */

View File

@ -28,7 +28,7 @@ float octahedral_texel_solid_angle(ivec2 local_texel,
/* Do not weight these border pixels that are redundant. */
return 0.0;
}
/* Since we are puting texel centers on the edges of the octahedron, the shape of a texel can be
/* Since we are pouting texel centers on the edges of the octahedron, the shape of a texel can be
* anything from a simple quad (at the Z=0 poles), to a 4 pointed start (at the Z=+-1 poles)
* passing by arrow tail shapes (at the X=0 and Y=0 edges). So while it would be more correct to
* account for all these shapes (using 8 triangles), it proves to be quite involved with all the
@ -54,7 +54,7 @@ float octahedral_texel_solid_angle(ivec2 local_texel,
v02 = normalize(v02);
v12 = normalize(v12);
v22 = normalize(v22);
#if 0 /* Has artifacts, is marginaly more correct. */
#if 0 /* Has artifacts, is marginally more correct. */
/* For some reason quad_solid_angle(v10, v20, v11, v21) gives some strange artifacts at Z=0. */
return 0.25 * (quad_solid_angle(v00, v10, v01, v11) + quad_solid_angle(v10, v20, v11, v21) +
quad_solid_angle(v01, v11, v02, v12) + quad_solid_angle(v11, v21, v12, v22));
@ -132,7 +132,7 @@ void main()
/* TODO(fclem): Cleanup: Should spherical_harmonics_encode_signal_sample return a new sh
* instead of adding to it? */
spherical_harmonics_encode_signal_sample(L, local_radiance[0], sh);
/* Outputs one SH for each threadgroup. */
/* Outputs one SH for each thread-group. */
uint work_group_index = gl_NumWorkGroups.x * gl_WorkGroupID.y + gl_WorkGroupID.x;
out_sh[work_group_index].L0_M0 = sh.L0.M0;
out_sh[work_group_index].L1_Mn1 = sh.L1.Mn1;

View File

@ -110,22 +110,8 @@ void shadow_tag_usage_tilemap_punctual(
/* TODO(fclem): 3D shift for jittered soft shadows. */
lP += vec3(0.0, 0.0, -light.shadow_projection_shift);
/* How much a shadow map pixel covers a final image pixel.
* We project a shadow map pixel (as a sphere for simplicity) to the receiver plane.
* We then reproject this sphere onto the camera screen and compare it to the film pixel size.
* This gives a good approximation of what LOD to select to get a somewhat uniform shadow map
* resolution in screen space. */
float footprint_ratio = dist_to_light;
/* Project the radius to the screen. 1 unit away from the camera the same way
* pixel_world_radius_inv was computed. Not needed in orthographic mode. */
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
if (is_persp) {
footprint_ratio /= dist_to_cam;
}
/* Apply resolution ratio. */
footprint_ratio *= tilemap_projection_ratio;
/* Take the frustum padding into account. */
footprint_ratio *= light.clip_side / orderedIntBitsToFloat(light.clip_near);
float footprint_ratio = shadow_punctual_footprint_ratio(
light, P, drw_view_is_perspective(), dist_to_cam, tilemap_proj_ratio);
if (radius == 0) {
int face_id = shadow_punctual_face_index_get(lP);

View File

@ -23,7 +23,7 @@ void main()
light.type = LIGHT_SUN;
light.clipmap_lod_min = -5;
light.clipmap_lod_max = 8;
light._clipmap_lod_bias = 0.0;
light.lod_bias = 0.0;
float fac = float(SHADOW_TILEMAP_RES - 1) / float(SHADOW_TILEMAP_RES);
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.0)), light.clipmap_lod_min);
EXPECT_EQ(shadow_directional_level(light, vec3(fac * 0.49)), 1);
@ -51,7 +51,7 @@ void main()
light._clipmap_origin_x = 0.0;
light._clipmap_origin_y = 0.0;
float half_size = exp2(float(light.clipmap_lod_min - 1));
light._clipmap_lod_bias = light.clipmap_lod_min - 1;
light.lod_bias = light.clipmap_lod_min - 1;
float fac = float(SHADOW_TILEMAP_RES - 1) / float(SHADOW_TILEMAP_RES);
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 0.0, 0.0, 0.0)), 2);
EXPECT_EQ(shadow_directional_level(light, vec3(fac * half_size * 0.5, 0.0, 0.0)), 2);
@ -76,7 +76,7 @@ void main()
light._position = vec3(0.0);
light._clipmap_origin_x = 0.0;
light._clipmap_origin_y = 0.0;
light._clipmap_lod_bias = 0;
light.lod_bias = 0;
float lod_min_tile_size = exp2(float(light.clipmap_lod_min)) / float(SHADOW_TILEMAP_RES);
float lod_max_half_size = exp2(float(light.clipmap_lod_max)) / 2.0;
@ -189,7 +189,7 @@ void main()
light.clipmap_lod_max = 2; /* 3 tile-maps. */
light.tilemap_index = 1;
light._position = vec3(0.0);
light._clipmap_lod_bias = light.clipmap_lod_min - 1;
light.lod_bias = light.clipmap_lod_min - 1;
light._clipmap_origin_x = 0.0;
light._clipmap_origin_y = 0.0;
float lod_tile_size = exp2(float(light.clipmap_lod_min)) / float(SHADOW_TILEMAP_RES);

View File

@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
/* ---------------------------------------------------------------------- */
/** \name Tile-map data
@ -106,7 +107,8 @@ ShadowTileData shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int tilem
* \a lP shading point position in light space, relative to the to camera position snapped to
* the smallest clip-map level (`shadow_world_to_local(light, P) - light._position`).
*/
int shadow_directional_level(LightData light, vec3 lP)
float shadow_directional_level_fractional(LightData light, vec3 lP)
{
float lod;
if (light.type == LIGHT_SUN) {
@ -124,8 +126,39 @@ int shadow_directional_level(LightData light, vec3 lP)
float lod_min_half_size = exp2(float(light.clipmap_lod_min - 1));
lod = length(lP.xy) * narrowing / lod_min_half_size;
}
int clipmap_lod = int(ceil(lod + light._clipmap_lod_bias));
return clamp(clipmap_lod, light.clipmap_lod_min, light.clipmap_lod_max);
float clipmap_lod = lod + light.lod_bias;
return clamp(clipmap_lod, float(light.clipmap_lod_min), float(light.clipmap_lod_max));
}
int shadow_directional_level(LightData light, vec3 lP)
{
return int(ceil(shadow_directional_level_fractional(light, lP)));
}
/* How much a tilemap pixel covers a final image pixel. */
float shadow_punctual_footprint_ratio(LightData light,
vec3 P,
bool is_perspective,
float dist_to_cam,
float tilemap_projection_ratio)
{
/* We project a shadow map pixel (as a sphere for simplicity) to the receiver plane.
* We then reproject this sphere onto the camera screen and compare it to the film pixel size.
* This gives a good approximation of what LOD to select to get a somewhat uniform shadow map
* resolution in screen space. */
float dist_to_light = distance(P, light._position);
float footprint_ratio = dist_to_light;
/* Project the radius to the screen. 1 unit away from the camera the same way
* pixel_world_radius_inv was computed. Not needed in orthographic mode. */
if (is_perspective) {
footprint_ratio /= dist_to_cam;
}
/* Apply resolution ratio. */
footprint_ratio *= tilemap_projection_ratio;
/* Take the frustum padding into account. */
footprint_ratio *= light.clip_side / orderedIntBitsToFloat(light.clip_near);
return footprint_ratio;
}
struct ShadowCoordinates {

View File

@ -412,7 +412,7 @@ SHADOW_MAP_TRACE_FN(ShadowRayPunctual)
/* Compute the world space offset of the shading position required for
* stochastic percentage closer filtering of shadow-maps. */
vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng)
vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng, vec2 random)
{
if (light.pcf_radius <= 0.001) {
/* Early return. */
@ -461,14 +461,27 @@ vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3
/* Compute the actual offset. */
vec2 rand = vec2(0.0);
#ifdef EEVEE_SAMPLING_DATA
rand = sampling_rng_2D_get(SAMPLING_SHADOW_V);
#endif
vec2 pcf_offset = interlieved_gradient_noise(UTIL_TEXEL, vec2(0.0), rand);
pcf_offset = pcf_offset * 2.0 - 1.0;
vec2 pcf_offset = random * 2.0 - 1.0;
pcf_offset *= light.pcf_radius;
/* Scale the offset based on shadow LOD. */
if (is_directional) {
vec3 lP = light_world_to_local(light, P);
float level = shadow_directional_level_fractional(light, lP - light._position);
float pcf_scale = mix(0.5, 1.0, fract(level));
pcf_offset *= pcf_scale;
}
else {
bool is_perspective = drw_view_is_perspective();
float dist_to_cam = distance(P, drw_view_position());
float footprint_ratio = shadow_punctual_footprint_ratio(
light, P, is_perspective, dist_to_cam, uniform_buf.shadow.tilemap_projection_ratio);
float lod = -log2(footprint_ratio) + light.lod_bias;
lod = clamp(lod, 0.0, float(SHADOW_TILEMAP_LOD));
float pcf_scale = exp2(lod);
pcf_offset *= pcf_scale;
}
vec3 ws_offset = TBN * vec3(pcf_offset, 0.0);
vec3 offset_P = P + ws_offset;
@ -515,17 +528,19 @@ ShadowEvalResult shadow_eval(LightData light,
# elif defined(GPU_COMPUTE_SHADER)
vec2 pixel = vec2(gl_GlobalInvocationID.xy);
# endif
vec3 random_shadow_3d = utility_tx_fetch(utility_tx, pixel, UTIL_BLUE_NOISE_LAYER).rgb;
random_shadow_3d += sampling_rng_3D_get(SAMPLING_SHADOW_U);
vec3 blue_noise_3d = utility_tx_fetch(utility_tx, pixel, UTIL_BLUE_NOISE_LAYER).rgb;
vec3 random_shadow_3d = blue_noise_3d + sampling_rng_3D_get(SAMPLING_SHADOW_U);
vec2 random_pcf_2d = fract(blue_noise_3d.xy + sampling_rng_2D_get(SAMPLING_SHADOW_X));
float normal_offset = uniform_buf.shadow.normal_bias;
#else
/* Case of surfel light eval. */
vec3 random_shadow_3d = vec3(0.5);
vec2 random_pcf_2d = vec2(0.0);
/* TODO(fclem): Parameter on irradiance volumes? */
float normal_offset = 0.02;
#endif
P += shadow_pcf_offset(light, is_directional, P, Ng);
P += shadow_pcf_offset(light, is_directional, P, Ng, random_pcf_2d);
/* Avoid self intersection. */
P = offset_ray(P, Ng);

View File

@ -14,9 +14,9 @@
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_transparency_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
vec4 closure_to_rgba(Closure cl)
{
@ -25,13 +25,15 @@ vec4 closure_to_rgba(Closure cl)
void main()
{
float f_depth = gl_FragCoord.z + fwidth(gl_FragCoord.z);
#ifdef MAT_TRANSPARENT
init_globals();
nodetree_surface(0.0);
float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY);
float random_threshold = transparency_hashed_alpha_threshold(1.0, noise_offset, g_data.P);
float random_threshold = pcg4d(vec4(g_data.P, noise_offset)).x;
float transparency = average(g_transmittance);
if (transparency > random_threshold) {
@ -40,8 +42,6 @@ void main()
}
#endif
float f_depth = gl_FragCoord.z + fwidth(gl_FragCoord.z);
#ifdef SHADOW_UPDATE_ATOMIC_RASTER
ivec2 texel_co = ivec2(gl_FragCoord.xy);

View File

@ -74,7 +74,7 @@ SphericalHarmonicL1 volume_phase_function_as_sh_L1(vec3 V, float g)
/* Compute rotated zonal harmonic.
* From Bartlomiej Wronsky
* "Volumetric Fog: Unified compute shader based solution to atmospheric scattering" page 55
* Siggraph 2014
* SIGGRAPH 2014
* https://bartwronski.files.wordpress.com/2014/08/bwronski_volumetric_fog_siggraph2014.pdf
*/
SphericalHarmonicL1 sh;

View File

@ -64,7 +64,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_opaque)
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(6, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
.push_constant(Type::FLOAT, "tilemap_proj_ratio")
.additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_light_data")
.compute_source("eevee_shadow_tag_usage_comp.glsl");
@ -75,7 +75,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_surfels)
/* ShadowTileDataPacked is uint. But MSL translation need the real type. */
.storage_buf(7, Qualifier::READ_WRITE, "uint", "tiles_buf[]")
.push_constant(Type::INT, "directional_level")
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
.push_constant(Type::FLOAT, "tilemap_proj_ratio")
.additional_info("eevee_shared",
"draw_view",
"draw_view_culling",
@ -96,7 +96,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_transparent)
.storage_buf(4, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(6, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
.push_constant(Type::FLOAT, "tilemap_proj_ratio")
.push_constant(Type::FLOAT, "pixel_world_radius")
.push_constant(Type::IVEC2, "fb_resolution")
.push_constant(Type::INT, "fb_lod")
@ -117,7 +117,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_volume)
.local_group_size(VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE, VOLUME_GROUP_SIZE)
.storage_buf(4, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(5, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
.push_constant(Type::FLOAT, "tilemap_proj_ratio")
.additional_info("eevee_volume_properties_data",
"eevee_shared",
"draw_view",

View File

@ -623,7 +623,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
cols_slice[idx]);
}
if (is_cyclic && points.size() >= 3) {
if (is_cyclic) {
const int idx = points.size() + 1;
const float length = points.size() > 1 ? lengths[points.size() - 1] : 0.0f;
populate_point(verts_range,

View File

@ -73,11 +73,10 @@ void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob)
nmd.node_group = ntreeAddTree(bmain, DATA_("Surface Deform"), "GeometryNodeTree");
bNodeTree *ntree = nmd.node_group;
ntree->tree_interface.add_socket("Geometry",
"",
"NodeSocketGeometry",
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
nullptr);
ntree->tree_interface.add_socket(
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
ntree->tree_interface.add_socket(
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
bNode *group_input = nodeAddStaticNode(&C, ntree, NODE_GROUP_INPUT);
bNode *group_output = nodeAddStaticNode(&C, ntree, NODE_GROUP_OUTPUT);
bNode *deform_node = nodeAddStaticNode(&C, ntree, GEO_NODE_DEFORM_CURVES_ON_SURFACE);

View File

@ -1224,9 +1224,9 @@ void ED_view3d_grid_steps(const Scene *scene,
* The actual code is seen in `object_grid_frag.glsl` (see `grid_res`).
* Currently the simulation is only done when RV3D_VIEW_IS_AXIS.
*/
float ED_view3d_grid_view_scale(Scene *scene,
View3D *v3d,
ARegion *region,
float ED_view3d_grid_view_scale(const Scene *scene,
const View3D *v3d,
const ARegion *region,
const char **r_grid_unit);
/**
@ -1287,8 +1287,8 @@ void ED_view3d_draw_bgpic_test(const Scene *scene,
/* view3d_gizmo_preselect_type.cc */
void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C,
wmGizmo *gz,
void ED_view3d_gizmo_mesh_preselect_get_active(const bContext *C,
const wmGizmo *gz,
Base **r_base,
BMElem **r_ele);
void ED_view3d_gizmo_mesh_preselect_clear(wmGizmo *gz);
@ -1305,8 +1305,8 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
* See if current UUID is valid, otherwise set a valid UUID to v3d,
* Try to keep the same UUID previously used to allow users to quickly toggle back and forth.
*/
bool ED_view3d_local_collections_set(Main *bmain, View3D *v3d);
void ED_view3d_local_collections_reset(bContext *C, bool reset_all);
bool ED_view3d_local_collections_set(const Main *bmain, View3D *v3d);
void ED_view3d_local_collections_reset(const bContext *C, bool reset_all);
#ifdef WITH_XR_OPENXR
void ED_view3d_xr_mirror_update(const ScrArea *area, const View3D *v3d, bool enable);

View File

@ -214,7 +214,7 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain
*/
virtual bool set_collapsed(bool collapsed);
/**
* Make this item be uncollapsed on first draw (may later be overriden by
* Make this item be uncollapsed on first draw (may later be overridden by
* #should_be_collapsed()). Must only be done during tree building.
*
* \note this does not call #on_collapse_change() or #set_collapsed() overrides.

View File

@ -178,8 +178,8 @@ class LayerViewItem : public AbstractTreeViewItem {
bool supports_collapsing() const override
{
/* This is a bit redundant since `LayerViewItem` can't have children. But being expplicit might
* catch errors. */
/* This is a bit redundant since `LayerViewItem` can't have children.
* But being explicit might catch errors. */
return false;
}

View File

@ -342,7 +342,7 @@ static void update_affected_nodes_by_clip_planes(GestureData &gesture_data)
case SelectionType::Outside:
/* Certain degenerate cases of a lasso shape can cause the resulting
* frustum planes to enclose a node's AABB, therefore we must submit it
* to be more throughly evaluated. */
* to be more thoroughly evaluated. */
if (gesture_data.shape_type == ShapeType::Lasso) {
return true;
}

View File

@ -38,9 +38,12 @@ int /*eContextResult*/ file_context(const bContext *C,
#define ATTRIBUTE_COLUMN_PADDING (0.5f * UI_UNIT_X)
#define FILE_LAYOUT_COMPACT(_layout) ((_layout->width / UI_SCALE_FAC) < 500)
#define FILE_LAYOUT_HIDE_DATE(_layout) ((_layout->width / UI_SCALE_FAC) < 250)
#define FILE_LAYOUT_HIDE_SIZE(_layout) ((_layout->width / UI_SCALE_FAC) < 350)
#define FILE_LAYOUT_COMPACT(_layout) \
(_layout->flag & FILE_LAYOUT_VER && (_layout->width / UI_SCALE_FAC) < 500)
#define FILE_LAYOUT_HIDE_DATE(_layout) \
(_layout->flag & FILE_LAYOUT_VER && (_layout->width / UI_SCALE_FAC) < 250)
#define FILE_LAYOUT_HIDE_SIZE(_layout) \
(_layout->flag & FILE_LAYOUT_VER && (_layout->width / UI_SCALE_FAC) < 350)
void file_calc_previews(const bContext *C, ARegion *region);
void file_draw_list(const bContext *C, ARegion *region);

View File

@ -2179,19 +2179,6 @@ static int graphkeys_select_key_handles_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static void graphkeys_select_key_handles_ui(bContext * /*C*/, wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *row;
row = uiLayoutRow(layout, false);
uiItemR(row, op->ptr, "left_handle_action", UI_ITEM_NONE, nullptr, ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemR(row, op->ptr, "right_handle_action", UI_ITEM_NONE, nullptr, ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemR(row, op->ptr, "key_action", UI_ITEM_NONE, nullptr, ICON_NONE);
}
void GRAPH_OT_select_key_handles(wmOperatorType *ot)
{
/* identifiers */
@ -2203,7 +2190,6 @@ void GRAPH_OT_select_key_handles(wmOperatorType *ot)
/* callbacks */
ot->poll = graphop_visible_keyframes_poll;
ot->exec = graphkeys_select_key_handles_exec;
ot->ui = graphkeys_select_key_handles_ui;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;

View File

@ -404,7 +404,7 @@ static void stats_update(Depsgraph *depsgraph,
memset(stats, 0x0, sizeof(*stats));
if (obedit) {
if (obedit && (ob->type != OB_GREASE_PENCIL)) {
/* Edit Mode. */
FOREACH_OBJECT_BEGIN (scene, view_layer, ob_iter) {
if (ob_iter->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) {
@ -829,7 +829,26 @@ void ED_info_draw_stats(
stats_row(col1, labels[OBJ], col2, stats_fmt.totobj, nullptr, y, height);
}
if (obedit) {
if (!any_selected) {
if (any_objects) {
/* Show scene totals if nothing is selected. */
stats_row(col1, labels[VERTS], col2, stats_fmt.totvert, nullptr, y, height);
stats_row(col1, labels[EDGES], col2, stats_fmt.totedge, nullptr, y, height);
stats_row(col1, labels[FACES], col2, stats_fmt.totface, nullptr, y, height);
stats_row(col1, labels[TRIS], col2, stats_fmt.tottri, nullptr, y, height);
}
else {
/* No objects in scene. */
stats_row(col1, labels[OBJ], col2, stats_fmt.totobj, nullptr, y, height);
}
}
else if ((ob) && ELEM(ob->type, OB_GPENCIL_LEGACY, OB_GREASE_PENCIL)) {
stats_row(col1, labels[LAYERS], col2, stats_fmt.totgplayer, nullptr, y, height);
stats_row(col1, labels[FRAMES], col2, stats_fmt.totgpframe, nullptr, y, height);
stats_row(col1, labels[STROKES], col2, stats_fmt.totgpstroke, nullptr, y, height);
stats_row(col1, labels[POINTS], col2, stats_fmt.totgppoint, nullptr, y, height);
}
else if (obedit) {
if (obedit->type == OB_MESH) {
stats_row(col1, labels[VERTS], col2, stats_fmt.totvertsel, stats_fmt.totvert, y, height);
stats_row(col1, labels[EDGES], col2, stats_fmt.totedgesel, stats_fmt.totedge, y, height);
@ -854,28 +873,9 @@ void ED_info_draw_stats(
stats_row(col1, labels[FACES], col2, stats_fmt.totfacesculpt, nullptr, y, height);
}
}
else if (!any_selected) {
if (any_objects) {
/* Show scene totals if nothing is selected. */
stats_row(col1, labels[VERTS], col2, stats_fmt.totvert, nullptr, y, height);
stats_row(col1, labels[EDGES], col2, stats_fmt.totedge, nullptr, y, height);
stats_row(col1, labels[FACES], col2, stats_fmt.totface, nullptr, y, height);
stats_row(col1, labels[TRIS], col2, stats_fmt.tottri, nullptr, y, height);
}
else {
/* No objects in scene. */
stats_row(col1, labels[OBJ], col2, stats_fmt.totobj, nullptr, y, height);
}
}
else if (ob && (object_mode & OB_MODE_POSE)) {
stats_row(col1, labels[BONES], col2, stats_fmt.totbonesel, stats_fmt.totbone, y, height);
}
else if ((ob) && ELEM(ob->type, OB_GPENCIL_LEGACY, OB_GREASE_PENCIL)) {
stats_row(col1, labels[LAYERS], col2, stats_fmt.totgplayer, nullptr, y, height);
stats_row(col1, labels[FRAMES], col2, stats_fmt.totgpframe, nullptr, y, height);
stats_row(col1, labels[STROKES], col2, stats_fmt.totgpstroke, nullptr, y, height);
stats_row(col1, labels[POINTS], col2, stats_fmt.totgppoint, nullptr, y, height);
}
else if ((ob) && (ob->type == OB_LAMP)) {
stats_row(col1, labels[LIGHTS], col2, stats_fmt.totlampsel, stats_fmt.totlamp, y, height);
}

View File

@ -373,54 +373,7 @@ static Vector<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
const SocketDeclaration &socket_decl = *socket_decl_ptr;
NodeLinkItem item;
item.socket_index = index++;
if (dynamic_cast<const decl::Float *>(&socket_decl)) {
item.socket_type = SOCK_FLOAT;
}
else if (dynamic_cast<const decl::Int *>(&socket_decl)) {
item.socket_type = SOCK_INT;
}
else if (dynamic_cast<const decl::Bool *>(&socket_decl)) {
item.socket_type = SOCK_BOOLEAN;
}
else if (dynamic_cast<const decl::Vector *>(&socket_decl)) {
item.socket_type = SOCK_VECTOR;
}
else if (dynamic_cast<const decl::Color *>(&socket_decl)) {
item.socket_type = SOCK_RGBA;
}
else if (dynamic_cast<const decl::Rotation *>(&socket_decl)) {
item.socket_type = SOCK_ROTATION;
}
else if (dynamic_cast<const decl::Matrix *>(&socket_decl)) {
item.socket_type = SOCK_MATRIX;
}
else if (dynamic_cast<const decl::String *>(&socket_decl)) {
item.socket_type = SOCK_STRING;
}
else if (dynamic_cast<const decl::Menu *>(&socket_decl)) {
item.socket_type = SOCK_MENU;
}
else if (dynamic_cast<const decl::Image *>(&socket_decl)) {
item.socket_type = SOCK_IMAGE;
}
else if (dynamic_cast<const decl::Texture *>(&socket_decl)) {
item.socket_type = SOCK_TEXTURE;
}
else if (dynamic_cast<const decl::Material *>(&socket_decl)) {
item.socket_type = SOCK_MATERIAL;
}
else if (dynamic_cast<const decl::Shader *>(&socket_decl)) {
item.socket_type = SOCK_SHADER;
}
else if (dynamic_cast<const decl::Collection *>(&socket_decl)) {
item.socket_type = SOCK_COLLECTION;
}
else if (dynamic_cast<const decl::Object *>(&socket_decl)) {
item.socket_type = SOCK_OBJECT;
}
else {
item.socket_type = SOCK_CUSTOM;
}
item.socket_type = socket_decl.socket_type;
item.socket_name = socket_decl.name.c_str();
item.node_name = arg->node_type->ui_name;
items.append(item);

View File

@ -578,10 +578,26 @@ static int preferences_extension_repo_remove_exec(bContext *C, wmOperator *op)
char dirpath[FILE_MAX];
BKE_preferences_extension_repo_dirpath_get(repo, dirpath, sizeof(dirpath));
if (dirpath[0] && BLI_is_dir(dirpath)) {
if (BLI_delete(dirpath, true, true) != 0) {
/* Removing custom directories has the potential to remove user data
* if users accidentally point this to their home directory or similar.
* Even though the UI shows a warning, we better prevent any accidents
* caused by recursive removal, see #119481.
* Only check custom directories because the non-custom directory is always
* a specific location under Blender's local extensions directory. */
const bool recursive = (repo->flag & USER_EXTENSION_REPO_FLAG_USE_CUSTOM_DIRECTORY) == 0;
/* Perform package manager specific clear operations,
* needed when `recursive` is false so the empty directory can be removed.
* If it's not empty there will be a warning that the directory couldn't be removed.
* The user will have to do this manually which is good since unknown files
* could be user data. */
BKE_callback_exec_string(bmain, BKE_CB_EVT_EXTENSION_REPOS_FILES_CLEAR, dirpath);
if (BLI_delete(dirpath, true, recursive) != 0) {
BKE_reportf(op->reports,
RPT_ERROR,
"Error removing directory: %s",
RPT_WARNING,
"Unable to remove directory: %s",
errno ? strerror(errno) : "unknown");
}
}
@ -688,9 +704,7 @@ static void PREFERENCES_OT_extension_repo_upgrade(wmOperatorType *ot)
/** \name Drop Extension Operator
* \{ */
static int preferences_extension_url_drop_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
static int preferences_extension_url_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
char *url = RNA_string_get_alloc(op->ptr, "url", nullptr, 0, nullptr);
const bool url_is_remote = STRPREFIX(url, "http://") || STRPREFIX(url, "https://") ||
@ -705,7 +719,7 @@ static int preferences_extension_url_drop_invoke(bContext *C,
PointerRNA props_ptr;
WM_operator_properties_create_ptr(&props_ptr, ot);
RNA_string_set(&props_ptr, "url", url);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, event);
WM_operator_properties_free(&props_ptr);
retval = OPERATOR_FINISHED;
}

View File

@ -914,9 +914,9 @@ void ED_view3d_grid_steps(const Scene *scene,
view3d_grid_steps_ex(scene, v3d, rv3d, r_grid_steps, nullptr, nullptr);
}
float ED_view3d_grid_view_scale(Scene *scene,
View3D *v3d,
ARegion *region,
float ED_view3d_grid_view_scale(const Scene *scene,
const View3D *v3d,
const ARegion *region,
const char **r_grid_unit)
{
float grid_scale;

View File

@ -493,8 +493,8 @@ void ED_gizmotypes_preselect_3d()
* the information from this gizmo.
* \{ */
void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C,
wmGizmo *gz,
void ED_view3d_gizmo_mesh_preselect_get_active(const bContext *C,
const wmGizmo *gz,
Base **r_base,
BMElem **r_ele)
{

View File

@ -9,7 +9,7 @@
#include "DNA_curve_types.h"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_rotation.h"
#include "BLI_math_vector.hh"
#include "BLI_rect.h"
@ -224,6 +224,7 @@ void ViewOpsData::init_navigation(bContext *C,
const float dyn_ofs_override[3],
const bool use_cursor_init)
{
using namespace blender;
this->nav_type = nav_type;
eViewOpsFlag viewops_flag = nav_type->flag & viewops_flag_from_prefs();
@ -285,21 +286,18 @@ void ViewOpsData::init_navigation(bContext *C,
float my_pivot[3]; /* View pivot. */
float dvec[3];
/* locals for dist correction */
float mat[3][3];
float upvec[3];
negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
/* Set the dist value to be the distance from this 3d point this means you'll
* always be able to zoom into it and panning won't go bad when dist was zero. */
/* remove dist value */
float3 upvec;
upvec[0] = upvec[1] = 0;
upvec[2] = rv3d->dist;
copy_m3_m4(mat, rv3d->viewinv);
float3x3 mat = float3x3(float4x4(rv3d->viewinv));
mul_m3_v3(mat, upvec);
upvec = math::transform_point(mat, upvec);
add_v3_v3v3(my_pivot, my_origin, upvec);
/* find a new ofs value that is along the view axis
@ -778,7 +776,8 @@ void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
{
static float lastofs[3] = {0, 0, 0};
using namespace blender;
static float3 lastofs = float3(0);
bool is_set = false;
const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@ -801,41 +800,39 @@ bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
Curve *cu = static_cast<Curve *>(ob_act_eval->data);
EditFont *ef = cu->editfont;
zero_v3(lastofs);
lastofs = float3(0);
for (int i = 0; i < 4; i++) {
add_v2_v2(lastofs, ef->textcurs[i]);
lastofs += ef->textcurs[i];
}
mul_v2_fl(lastofs, 1.0f / 4.0f);
lastofs *= 0.25f;
mul_m4_v3(ob_act_eval->object_to_world().ptr(), lastofs);
lastofs = math::transform_point(ob_act_eval->object_to_world(), lastofs);
is_set = true;
}
else if (ob_act == nullptr || ob_act->mode == OB_MODE_OBJECT) {
/* Object mode uses bounding-box centers. */
uint tot = 0;
float select_center[3];
int total = 0;
float3 select_center(0);
zero_v3(select_center);
LISTBASE_FOREACH (Base *, base_eval, BKE_view_layer_object_bases_get(view_layer_eval)) {
LISTBASE_FOREACH (const Base *, base_eval, BKE_view_layer_object_bases_get(view_layer_eval)) {
if (BASE_SELECTED(v3d, base_eval)) {
/* Use the bounding-box if we can. */
Object *ob_eval = base_eval->object;
const Object *ob_eval = base_eval->object;
if (ob_eval->runtime->bounds_eval) {
blender::float3 cent = blender::math::midpoint(ob_eval->runtime->bounds_eval->min,
ob_eval->runtime->bounds_eval->max);
mul_m4_v3(ob_eval->object_to_world().ptr(), cent);
add_v3_v3(select_center, cent);
if (const std::optional<Bounds<float3>> bounds = BKE_object_boundbox_get(ob_eval)) {
const float3 center = math::midpoint(bounds->min, bounds->max);
select_center += math::transform_point(ob_eval->object_to_world(), center);
}
else {
add_v3_v3(select_center, ob_eval->object_to_world().location());
}
tot++;
total++;
}
}
if (tot) {
mul_v3_fl(select_center, 1.0f / float(tot));
if (total) {
mul_v3_fl(select_center, 1.0f / float(total));
copy_v3_v3(lastofs, select_center);
is_set = true;
}

View File

@ -1147,7 +1147,9 @@ void VIEW3D_OT_localview_remove_from(wmOperatorType *ot)
/** \name Local Collections
* \{ */
static uint free_localcollection_bit(Main *bmain, ushort local_collections_uid, bool *r_reset)
static uint free_localcollection_bit(const Main *bmain,
ushort local_collections_uid,
bool *r_reset)
{
ushort local_view_bits = 0;
@ -1196,7 +1198,7 @@ static void local_collections_reset_uuid(LayerCollection *layer_collection,
}
}
static void view3d_local_collections_reset(Main *bmain, const uint local_view_bit)
static void view3d_local_collections_reset(const Main *bmain, const uint local_view_bit)
{
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
@ -1207,7 +1209,7 @@ static void view3d_local_collections_reset(Main *bmain, const uint local_view_bi
}
}
bool ED_view3d_local_collections_set(Main *bmain, View3D *v3d)
bool ED_view3d_local_collections_set(const Main *bmain, View3D *v3d)
{
if ((v3d->flag & V3D_LOCAL_COLLECTIONS) == 0) {
return true;
@ -1231,7 +1233,7 @@ bool ED_view3d_local_collections_set(Main *bmain, View3D *v3d)
return true;
}
void ED_view3d_local_collections_reset(bContext *C, const bool reset_all)
void ED_view3d_local_collections_reset(const bContext *C, const bool reset_all)
{
Main *bmain = CTX_data_main(C);
uint local_view_bit = ~(0);

View File

@ -1478,7 +1478,7 @@ bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], floa
}
}
/* Aftertrans does insert keyframes, and clears base flags; doesn't read transdata. */
/* Does insert keyframes, and clears base flags; doesn't read `transdata`. */
special_aftertrans_update(C, t);
postTrans(C, t);
@ -2221,7 +2221,7 @@ int transformEnd(bContext *C, TransInfo *t)
exit_code = OPERATOR_FINISHED;
}
/* Aftertrans does insert keyframes, and clears base flags; doesn't read transdata. */
/* Does insert keyframes, and clears base flags; doesn't read `transdata`. */
special_aftertrans_update(C, t);
/* Free data, also handles overlap [in freeTransCustomData()]. */

View File

@ -899,6 +899,12 @@ static eSnapMode snap_obj_fn(SnapObjectContext *sctx,
}
if (GS(ob_data->name) == ID_ME) {
if (ob_eval->type == OB_CURVES_LEGACY && BKE_object_is_in_editmode(ob_eval)) {
/* Sometimes, such as when Mesh is generated by Geometry Nodes, a Curve object may have Mesh
* instances.
* In these cases, skip the snap to Mesh if the Curve is in edit mode. */
return SCE_SNAP_TO_NONE;
}
return snap_object_mesh(sctx, ob_eval, ob_data, obmat, sctx->runtime.snap_to_flag, use_hide);
}

View File

@ -15,7 +15,7 @@ namespace blender::geometry::boolean {
/** Specifies which solver to use. */
enum class Solver {
/**
* The exact solver based on the Mesh Arrangments for Solid Geometry paper,
* The exact solver based on the Mesh Arrangements for Solid Geometry paper,
* by Zhou, Grinspun, Zorin, and Jacobson.
*/
MeshArr = 0,
@ -54,9 +54,9 @@ struct BooleanOpParameters {
* If there are more than two meshes, the first mesh is operand 0 and the rest of the
* meshes are operand 1 (i.e., as if all of operands 1, ... are joined into one mesh.
* The exact solvers assume that the meshes are PWN (piecewise winding number,
* which approximately means that the meshes are enclosed watertight voluems,
* which approximately means that the meshes are enclosed watertight volumes,
* and all edges are manifold, though there are allowable exceptions to that last condition).
* If the meshes don't sastisfy those conditions, all solvers will try to use ray-shooting
* If the meshes don't satisfy those conditions, all solvers will try to use ray-shooting
* to determine whether particular faces survive or not. This may or may not work
* in the way the user hopes.
*

View File

@ -1000,7 +1000,7 @@ static BMesh *mesh_bm_concat(Span<const Mesh *> meshes,
*r_looptris = looptris;
*r_looptris_tot = looptris_tot;
/* Tranform the vertices that into the desired target_transform space. */
/* Transform the vertices that into the desired target_transform space. */
BMIter iter;
BMVert *eve;
int i = 0;
@ -1131,7 +1131,7 @@ static Mesh *mesh_boolean_float(Span<const Mesh *> meshes,
BM_mesh_free(bm);
if (prev_result_mesh != nullptr) {
/* Except in the first iteration, two_meshes[0] holds the intermediate
* mesh result from the previous iteraiton. */
* mesh result from the previous iteration. */
BKE_mesh_eval_delete(prev_result_mesh);
}
if (i < meshes.size() - 2) {

View File

@ -1691,7 +1691,7 @@ void gpu::MTLTexture::read_internal(int mip,
/** Determine source read texture handle. */
id<MTLTexture> read_texture = texture_;
/* Use textureview handle if reading from a GPU texture view. */
/* Use texture-view handle if reading from a GPU texture view. */
if (resource_mode_ == MTL_TEXTURE_MODE_TEXTURE_VIEW) {
read_texture = this->get_metal_handle();
}

View File

@ -174,7 +174,7 @@ void GLVaoCache::clear()
if (context_) {
context_->vao_cache_unregister(this);
}
/* Reinit. */
/* Reinitialize. */
this->init();
}

View File

@ -232,3 +232,67 @@ float wang_hash_noise(uint s)
return fract(float(s) / 4294967296.0);
}
/* PCG */
/* https://www.pcg-random.org/ */
uint pcg_uint(uint u)
{
uint state = u * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
float pcg(float v)
{
return pcg_uint(floatBitsToUint(v)) / float(0xffffffffU);
}
float pcg(vec2 v)
{
/* Nested pcg (faster and better quality that pcg2d). */
uvec2 u = floatBitsToUint(v);
return pcg_uint(pcg_uint(u.x) + u.y) / float(0xffffffffU);
}
/* http://www.jcgt.org/published/0009/03/02/ */
vec3 pcg3d(vec3 v)
{
uvec3 u = floatBitsToUint(v);
u = u * 1664525u + 1013904223u;
u.x += u.y * u.z;
u.y += u.z * u.x;
u.z += u.x * u.y;
u ^= u >> 16u;
u.x += u.y * u.z;
u.y += u.z * u.x;
u.z += u.x * u.y;
return vec3(u) / float(0xffffffffU);
}
/* http://www.jcgt.org/published/0009/03/02/ */
vec4 pcg4d(vec4 v)
{
uvec4 u = floatBitsToUint(v);
u = u * 1664525u + 1013904223u;
u.x += u.y * u.w;
u.y += u.z * u.x;
u.z += u.x * u.y;
u.w += u.y * u.z;
u ^= u >> 16u;
u.x += u.y * u.w;
u.y += u.z * u.x;
u.z += u.x * u.y;
u.w += u.y * u.z;
return vec4(u) / float(0xffffffffU);
}

View File

@ -2,7 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#ifdef USE_GEOMETRY_SHADER
#if defined(USE_GEOMETRY_SHADER) || defined(USE_GEOMETRY_IFACE_COLOR)
vec4 fragment_in_color()
{
return geometry_out.mColor;

View File

@ -97,13 +97,13 @@ bool is_equal(vec4 p1, vec4 p2)
geometry_out.mTexCoord = vec2(0, 0); \
geometry_out.mColor = finalColor[1]; \
gl_Position = vec4( \
(sp1 + finalThickness[2] * n0) / gpencil_stroke_data.viewport, getZdepth(P1), 1.0);
(sp1 + finalThickness[1] * n0) / gpencil_stroke_data.viewport, getZdepth(P1), 1.0);
#define V1_a \
geometry_out.mTexCoord = vec2(0, 0); \
geometry_out.mColor = finalColor[1]; \
gl_Position = vec4( \
(sp1 + finalThickness[2] * n1) / gpencil_stroke_data.viewport, getZdepth(P1), 1.0);
(sp1 + finalThickness[1] * n1) / gpencil_stroke_data.viewport, getZdepth(P1), 1.0);
#define V2_a \
geometry_out.mTexCoord = vec2(0, 0.5); \
@ -114,12 +114,12 @@ bool is_equal(vec4 p1, vec4 p2)
geometry_out.mTexCoord = vec2(0, 1); \
geometry_out.mColor = finalColor[1]; \
gl_Position = vec4( \
(sp1 - finalThickness[2] * n1) / gpencil_stroke_data.viewport, getZdepth(P1), 1.0);
(sp1 - finalThickness[1] * n1) / gpencil_stroke_data.viewport, getZdepth(P1), 1.0);
#define V1_b \
geometry_out.mTexCoord = vec2(0, 1); \
geometry_out.mColor = finalColor[1]; \
gl_Position = vec4( \
(sp1 - finalThickness[2] * n0) / gpencil_stroke_data.viewport, getZdepth(P1), 1.0);
(sp1 - finalThickness[1] * n0) / gpencil_stroke_data.viewport, getZdepth(P1), 1.0);
#define V2_b \
geometry_out.mTexCoord = vec2(0, 0.5); \
@ -197,6 +197,11 @@ bool is_equal(vec4 p1, vec4 p2)
vec2 svn2 = normalize(sp2 - sp1) * length_b * 4.0 * extend; \
gl_Position = vec4((sp2 + svn2) / gpencil_stroke_data.viewport, getZdepth(P2), 1.0);
vec4 uchar4_to_normalized_vec4(uchar4 udata)
{
return vec4(udata) / vec4(255.0f);
}
void main(void)
{
/* Determine output geometry IDs. */
@ -215,7 +220,9 @@ void main(void)
for (int i = 0; i < 4; i++) {
finalPos[i] = ModelViewProjectionMatrix *
vec4(vertex_fetch_attribute(input_prim_id + i, pos, vec3).xyz, 1.0);
finalColor[i] = vertex_fetch_attribute(input_prim_id + i, color, vec4);
/* Color attribute uses GPU_FETCH_INT_TO_FLOAT_UNIT with GPU_COMP_U8. */
finalColor[i] = uchar4_to_normalized_vec4(
vertex_fetch_attribute(input_prim_id + i, color, uchar4));
float in_thickness = vertex_fetch_attribute(input_prim_id + i, thickness, float);
if (gpencil_stroke_data.keep_size) {
@ -223,7 +230,7 @@ void main(void)
}
else {
float size = (ProjectionMatrix[3][3] == 0.0) ?
(in_thickness / (gl_Position.z * defaultpixsize)) :
(in_thickness / (finalPos[i].z * defaultpixsize)) :
(in_thickness / defaultpixsize);
finalThickness[i] = max(size * gpencil_stroke_data.objscale, 1.0);
}
@ -296,8 +303,14 @@ void main(void)
}
/** Geometry output. */
/* First triangle (T0). prevent excessively long miters at sharp
* corners */
/* prevent excessively long miters at sharp corners */
if (dot(v0, v1) < -MiterLimit) {
miter_a = n1;
length_a = finalThickness[1];
}
/* First triangle (T0). */
if (output_prim_triangle_id == 0) {
if (dot(v0, v1) < -MiterLimit) {
if (dot(v0, n1) > 0) {

View File

@ -39,6 +39,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke)
GPU_SHADER_CREATE_INFO(gpu_shader_gpencil_stroke_no_geom)
.metal_backend_only(true)
.define("USE_GEOMETRY_IFACE_COLOR")
.additional_info("gpu_shader_gpencil_stroke_base")
.vertex_out(gpencil_stroke_geom_iface)
.vertex_source("gpu_shader_gpencil_stroke_vert_no_geom.glsl")

View File

@ -673,7 +673,7 @@ void colormanagement_init()
}
}
/* Then try bunded config file. */
/* Then try bundled configuration file. */
if (config == nullptr) {
const std::optional<std::string> configdir = BKE_appdir_folder_id(BLENDER_DATAFILES,
"colormanagement");
@ -3952,6 +3952,19 @@ bool IMB_colormanagement_processor_is_noop(ColormanageProcessor *cm_processor)
return false;
}
if (!cm_processor->cpu_processor) {
/* The CPU processor might have failed to be created, for example when the requested color
* space does not exist in the configuration, or if there is a missing lookup table, or the
* configuration is invalid due to other reasons.
*
* The actual processing checks for the cpu_processor not being null pointer, and it if is then
* processing does not apply it. However, processing could still apply curve mapping.
*
* Hence a null-pointer here, which happens after the curve mapping check, but before accessing
* cpu_processor. */
return true;
}
return OCIO_cpuProcessorIsNoOp(cm_processor->cpu_processor);
}

View File

@ -158,16 +158,17 @@ void USDPointInstancerReader::read_object_data(Main *bmain, const double motionS
ModifierData *md = BKE_modifier_new(eModifierType_Nodes);
BLI_addtail(&object_->modifiers, md);
BKE_modifiers_persistent_uid_init(*object_, *md);
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md);
nmd.node_group = ntreeAddTree(bmain, "Instances", "GeometryNodeTree");
bNodeTree *ntree = nmd.node_group;
ntree->tree_interface.add_socket("Geometry",
"",
"NodeSocketGeometry",
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
nullptr);
ntree->tree_interface.add_socket(
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
ntree->tree_interface.add_socket(
"Geometry", "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
bNode *group_input = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_INPUT);
group_input->locx = -400.0f;
bNode *group_output = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_OUTPUT);

View File

@ -44,6 +44,7 @@
.shadow_softness_factor = 1.0f, \
.shadow_trace_distance = 10.0f, \
.shadow_filter_radius = 3.0f, \
.shadow_resolution_scale = 1.0f, \
.att_dist = 40.0f, \
.sun_angle = DEG2RADF(0.526f), \
.area_spread = DEG2RADF(180.0f), \

View File

@ -78,6 +78,9 @@ typedef struct Light {
float shadow_softness_factor;
float shadow_trace_distance;
float shadow_filter_radius;
float shadow_resolution_scale;
float _pad0;
/* Preview */
struct PreviewImage *preview;

View File

@ -1019,7 +1019,7 @@
.strength = 1.0f, \
.skip = 0, \
}
#define _DNA_DEFAULT_GreasePencilOutlineModifierData \
{ \
.flag = MOD_GREASE_PENCIL_OUTLINE_KEEP_SHAPE, \
@ -1044,4 +1044,22 @@
.smooth_step = 1, \
}
/* Here we deliberately set effective range to the half the default
* frame-range to have an immediate effect to suggest use-cases. */
#define _DNA_DEFAULT_GreasePencilBuildModifierData \
{ \
.start_frame = 1, \
.end_frame = 125, \
.start_delay = 0.0f, \
.length = 100.0f, \
.flag = 0, \
.mode = 0, \
.transition = 0, \
.time_alignment = 0, \
.time_mode = 0, \
.speed_fac = 1.2f, \
.speed_maxgap = 0.5f, \
.percentage_fac = 0.0f, \
}
/* clang-format off */

View File

@ -118,6 +118,7 @@ typedef enum ModifierType {
eModifierType_GreasePencilEnvelope = 81,
eModifierType_GreasePencilOutline = 82,
eModifierType_GreasePencilShrinkwrap = 83,
eModifierType_GreasePencilBuild = 84,
NUM_MODIFIER_TYPES,
} ModifierType;
@ -3310,3 +3311,96 @@ typedef struct GreasePencilShrinkwrapModifierData {
/** Runtime only. */
struct ShrinkwrapTreeData *cache_data;
} GreasePencilShrinkwrapModifierData;
typedef struct GreasePencilBuildModifierData {
ModifierData modifier;
GreasePencilModifierInfluenceData influence;
/**
* If GP_BUILD_RESTRICT_TIME is set,
* the defines the frame range where GP frames are considered.
*/
float start_frame;
float end_frame;
/** Start time added on top of the drawing frame number */
float start_delay;
float length;
/** #GreasePencilBuildFlag. */
short flag;
/** #GreasePencilBuildMode. */
short mode;
/** #GreasePencilBuildTransition. */
short transition;
/**
* #GreasePencilBuildTimeAlignment.
* For the "Concurrent" mode, when should "shorter" strips start/end.
*/
short time_alignment;
/** Speed factor for #GP_BUILD_TIMEMODE_DRAWSPEED. */
float speed_fac;
/** Maximum time gap between strokes for #GP_BUILD_TIMEMODE_DRAWSPEED. */
float speed_maxgap;
/** GreasePencilBuildTimeMode. */
short time_mode;
char _pad[6];
/** Build origin control object. */
struct Object *object;
/** Factor of the stroke (used instead of frame evaluation). */
float percentage_fac;
/** Weight fading at the end of the stroke. */
float fade_fac;
/** Target vertex-group name, #MAX_VGROUP_NAME. */
char target_vgname[64];
/** Fading strength of opacity and thickness */
float fade_opacity_strength;
float fade_thickness_strength;
} GreasePencilBuildModifierData;
typedef enum GreasePencilBuildMode {
/* Strokes are shown one by one until all have appeared */
MOD_GREASE_PENCIL_BUILD_MODE_SEQUENTIAL = 0,
/* All strokes start at the same time */
MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT = 1,
/* Only the new strokes are built */
MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE = 2,
} GreasePencilBuildMode;
typedef enum GreasePencilBuildTransition {
/* Show in forward order */
MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW = 0,
/* Hide in reverse order */
MOD_GREASE_PENCIL_BUILD_TRANSITION_SHRINK = 1,
/* Hide in forward order */
MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH = 2,
} GreasePencilBuildTransition;
typedef enum GreasePencilBuildTimeAlignment {
/* All strokes start at same time */
MOD_GREASE_PENCIL_BUILD_TIMEALIGN_START = 0,
/* All strokes end at same time */
MOD_GREASE_PENCIL_BUILD_TIMEALIGN_END = 1,
/* TODO: Random Offsets, Stretch-to-Fill */
} GreasePencilBuildTimeAlignment;
typedef enum GreasePencilBuildTimeMode {
/** Use a number of frames build. */
MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES = 0,
/** Use manual percentage to build. */
MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE = 1,
/** Use factor of recorded speed to build. */
MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED = 2,
} GreasePencilBuildTimeMode;
typedef enum GreasePencilBuildFlag {
/* Restrict modifier to only operating between the nominated frames */
MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME = (1 << 0),
MOD_GREASE_PENCIL_BUILD_USE_FADING = (1 << 14),
} GreasePencilBuildFlag;

View File

@ -2887,6 +2887,7 @@ enum {
SCE_EEVEE_DOF_JITTER = (1 << 23),
SCE_EEVEE_SHADOW_ENABLED = (1 << 24),
SCE_EEVEE_RAYTRACE_OPTIONS_SPLIT = (1 << 25),
SCE_EEVEE_SHADOW_JITTERED_VIEWPORT = (1 << 26),
};
typedef enum RaytraceEEVEE_Flag {

View File

@ -305,6 +305,7 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilSubdivModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilNoiseModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilLengthModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilLineartModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilBuildModifierData);
/* Grease Pencil 3.0 modifiers. */
SDNA_DEFAULT_DECL_STRUCT(GreasePencilSmoothModifierData);
@ -581,6 +582,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilLengthModifierData),
SDNA_DEFAULT_DECL(GreasePencilHookModifierData),
SDNA_DEFAULT_DECL(GreasePencilLineartModifierData),
SDNA_DEFAULT_DECL(GreasePencilBuildModifierData),
/* Grease Pencil 3.0 defaults. */
SDNA_DEFAULT_DECL(GreasePencilSmoothModifierData),

View File

@ -304,6 +304,17 @@ static void rna_def_light_shadow(StructRNA *srna, bool sun)
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_Light_update");
prop = RNA_def_property(srna, "shadow_resolution_scale", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 2.0f);
RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.25f, 2);
RNA_def_property_ui_text(
prop,
"Shadow Resolution Scale",
"Scale the Shadow Map target resolution, where 1.0 tries to match shadow map and screen "
"pixel density. (The scale is applied on top of the scene Simplify Shadow Resolution)");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_Light_update");
if (sun) {
prop = RNA_def_property(srna, "shadow_cascade_max_distance", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, nullptr, "cascade_max_dist");

View File

@ -234,6 +234,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_MOD_ARRAY,
"Array strokes",
"Duplicate strokes into an array"},
{eModifierType_GreasePencilBuild,
"GREASE_PENCIL_BUILD",
ICON_MOD_BUILD,
"Build",
"Grease Pencil build modifier"},
{eModifierType_GreasePencilLength,
"GREASE_PENCIL_LENGTH",
ICON_MOD_LENGTH,
@ -784,6 +789,25 @@ const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem grease_pencil_build_time_mode_items[] = {
{MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED,
"DRAWSPEED",
0,
"Natural Drawing Speed",
"Use recorded speed multiplied by a factor"},
{MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES,
"FRAMES",
0,
"Number of Frames",
"Set a fixed number of frames for all build animations"},
{MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE,
"PERCENTAGE",
0,
"Percentage Factor",
"Set a manual percentage to build"},
{0, nullptr, 0, nullptr, nullptr},
};
#ifdef RNA_RUNTIME
# include <algorithm>
@ -944,6 +968,7 @@ RNA_MOD_VGROUP_NAME_SET(Wireframe, defgrp_name);
RNA_MOD_VGROUP_NAME_SET(GreasePencilWeightAngle, target_vgname);
RNA_MOD_VGROUP_NAME_SET(GreasePencilWeightProximity, target_vgname);
RNA_MOD_VGROUP_NAME_SET(GreasePencilLineart, vgname);
RNA_MOD_VGROUP_NAME_SET(GreasePencilBuild, target_vgname);
static void rna_ExplodeModifier_vgroup_get(PointerRNA *ptr, char *value)
{
@ -1029,6 +1054,7 @@ RNA_MOD_OBJECT_SET(GreasePencilArmature, object, OB_ARMATURE);
RNA_MOD_OBJECT_SET(GreasePencilOutline, object, OB_EMPTY);
RNA_MOD_OBJECT_SET(GreasePencilShrinkwrap, target, OB_MESH);
RNA_MOD_OBJECT_SET(GreasePencilShrinkwrap, aux_target, OB_MESH);
RNA_MOD_OBJECT_SET(GreasePencilBuild, object, OB_EMPTY);
static void rna_HookModifier_object_set(PointerRNA *ptr,
PointerRNA value,
@ -1969,6 +1995,7 @@ RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilHook);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilEnvelope);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOutline);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilShrinkwrap);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilBuild);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOffset);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity);
@ -2089,6 +2116,35 @@ static void rna_GreasePencilDashModifier_segments_begin(CollectionPropertyIterat
nullptr);
}
const EnumPropertyItem *grease_pencil_build_time_mode_filter(bContext * /*C*/,
PointerRNA *ptr,
PropertyRNA * /*prop*/,
bool *r_free)
{
auto *md = static_cast<ModifierData *>(ptr->data);
auto *mmd = reinterpret_cast<BuildGpencilModifierData *>(md);
const bool is_concurrent = (mmd->mode == MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT);
EnumPropertyItem *item_list = nullptr;
int totitem = 0;
for (const EnumPropertyItem *item = grease_pencil_build_time_mode_items;
item->identifier != nullptr;
item++)
{
if (is_concurrent && (item->value == MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED)) {
continue;
}
RNA_enum_item_add(&item_list, &totitem, item);
}
RNA_enum_item_end(&item_list, &totitem);
*r_free = true;
return item_list;
}
static const GreasePencilTimeModifierData *find_grease_pencil_time_modifier_of_segment(
const Object &ob, const GreasePencilTimeModifierSegment &time_segment)
{
@ -10444,6 +10500,217 @@ static void rna_def_modifier_grease_pencil_shrinkwrap(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_build(BlenderRNA *brna)
{
static EnumPropertyItem prop_gpencil_build_mode_items[] = {
{MOD_GREASE_PENCIL_BUILD_MODE_SEQUENTIAL,
"SEQUENTIAL",
0,
"Sequential",
"Strokes appear/disappear one after the other, but only a single one changes at a time"},
{MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT,
"CONCURRENT",
0,
"Concurrent",
"Multiple strokes appear/disappear at once"},
{MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE,
"ADDITIVE",
0,
"Additive",
"Builds only new strokes (assuming 'additive' drawing)"},
{0, nullptr, 0, nullptr, nullptr},
};
static EnumPropertyItem prop_gpencil_build_transition_items[] = {
{MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW,
"GROW",
0,
"Grow",
"Show points in the order they occur in each stroke "
"(e.g. for animating lines being drawn)"},
{MOD_GREASE_PENCIL_BUILD_TRANSITION_SHRINK,
"SHRINK",
0,
"Shrink",
"Hide points from the end of each stroke to the start "
"(e.g. for animating lines being erased)"},
{MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH,
"FADE", /* "Fade" is the original id string kept for compatibility purpose. */
0,
"Vanish",
"Hide points in the order they occur in each stroke "
"(e.g. for animating ink fading or vanishing after getting drawn)"},
{0, nullptr, 0, nullptr, nullptr},
};
static EnumPropertyItem prop_gpencil_build_time_align_items[] = {
{MOD_GREASE_PENCIL_BUILD_TIMEALIGN_START,
"START",
0,
"Align Start",
"All strokes start at same time (i.e. short strokes finish earlier)"},
{MOD_GREASE_PENCIL_BUILD_TIMEALIGN_END,
"END",
0,
"Align End",
"All strokes end at same time (i.e. short strokes start later)"},
{0, nullptr, 0, nullptr, nullptr},
};
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "GreasePencilBuildModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Build Modifier", "Animate strokes appearing and disappearing");
RNA_def_struct_sdna(srna, "GreasePencilBuildModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_BUILD);
rna_def_modifier_grease_pencil_layer_filter(srna);
rna_def_modifier_grease_pencil_material_filter(
srna, "rna_GreasePencilBuildModifier_material_filter_set");
rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 0);
rna_def_modifier_panel_open_prop(srna, "open_frame_range_panel", 1);
rna_def_modifier_panel_open_prop(srna, "open_fading_panel", 2);
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_gpencil_build_mode_items);
RNA_def_property_ui_text(prop, "Mode", "How strokes are being built");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "transition", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_gpencil_build_transition_items);
RNA_def_property_ui_text(
prop, "Transition", "How are strokes animated (i.e. are they appearing or disappearing)");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "start_delay", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "start_delay");
RNA_def_property_ui_text(
prop, "Delay", "Number of frames after each GP keyframe before the modifier has any effect");
RNA_def_property_range(prop, 0, MAXFRAMEF);
RNA_def_property_ui_range(prop, 0, 200, 1, -1);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "length");
RNA_def_property_ui_text(prop,
"Length",
"Maximum number of frames that the build effect can run for "
"(unless another GP keyframe occurs before this time has elapsed)");
RNA_def_property_range(prop, 1, MAXFRAMEF);
RNA_def_property_ui_range(prop, 1, 1000, 1, -1);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "concurrent_time_alignment", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "time_alignment");
RNA_def_property_enum_items(prop, prop_gpencil_build_time_align_items);
RNA_def_property_ui_text(prop, "Time Alignment", "How should strokes start to appear/disappear");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "time_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "time_mode");
RNA_def_property_enum_items(prop, grease_pencil_build_time_mode_items);
RNA_def_property_enum_funcs(prop, nullptr, nullptr, "grease_pencil_build_time_mode_filter");
RNA_def_property_ui_text(
prop,
"Timing",
"Use drawing speed, a number of frames, or a manual factor to build strokes");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
/* Speed factor for MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED. */
/* Todo: Does it work? */
prop = RNA_def_property(srna, "speed_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "speed_fac");
RNA_def_property_ui_text(prop, "Speed Factor", "Multiply recorded drawing speed by a factor");
RNA_def_property_range(prop, 0.0f, 100.0f);
RNA_def_property_ui_range(prop, 0, 5, 0.001, -1);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
/* Max gap in seconds between strokes for MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED. */
prop = RNA_def_property(srna, "speed_maxgap", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "speed_maxgap");
RNA_def_property_ui_text(prop, "Maximum Gap", "The maximum gap between strokes in seconds");
RNA_def_property_range(prop, 0.0f, 100.0f);
RNA_def_property_ui_range(prop, 0, 4, 0.01, -1);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_restrict_frame_range", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME);
RNA_def_property_ui_text(
prop, "Restrict Frame Range", "Only modify strokes during the specified frame range");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
/* Use percentage bool (used by sequential & concurrent modes) */
prop = RNA_def_property(srna, "use_percentage", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "time_mode", MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE);
RNA_def_property_ui_text(
prop, "Restrict Visible Points", "Use a percentage factor to determine the visible points");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "percentage_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "percentage_fac");
RNA_def_property_ui_text(prop, "Factor", "Defines how much of the stroke is visible");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "start_frame");
RNA_def_property_ui_text(
prop, "Start Frame", "Start Frame (when Restrict Frame Range is enabled)");
RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "end_frame");
RNA_def_property_ui_text(prop, "End Frame", "End Frame (when Restrict Frame Range is enabled)");
RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", MOD_GREASE_PENCIL_BUILD_USE_FADING);
RNA_def_property_ui_text(prop, "Use Fading", "Fade out strokes instead of directly cutting off");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "fade_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "fade_fac");
RNA_def_property_ui_text(prop, "Fade Factor", "Defines how much of the stroke is fading in/out");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "target_vertex_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "target_vgname");
RNA_def_property_ui_text(prop, "Vertex Group", "Output Vertex group");
RNA_def_property_string_funcs(
prop, nullptr, nullptr, "rna_GreasePencilBuildModifier_target_vgname_set");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "fade_opacity_strength", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "fade_opacity_strength");
RNA_def_property_ui_text(
prop, "Opacity Strength", "How much strength fading applies on top of stroke opacity");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "fade_thickness_strength", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "fade_thickness_strength");
RNA_def_property_ui_text(
prop, "Thickness Strength", "How much strength fading applies on top of stroke thickness");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Object used as build starting position");
RNA_def_property_pointer_funcs(
prop, nullptr, "rna_GreasePencilBuildModifier_object_set", nullptr, nullptr);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
RNA_define_lib_overridable(false);
}
void RNA_def_modifier(BlenderRNA *brna)
{
StructRNA *srna;
@ -10636,6 +10903,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_grease_pencil_envelope(brna);
rna_def_modifier_grease_pencil_outline(brna);
rna_def_modifier_grease_pencil_shrinkwrap(brna);
rna_def_modifier_grease_pencil_build(brna);
}
#endif

View File

@ -2071,6 +2071,10 @@ static void object_simplify_update(Scene *scene,
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
}
if (ob->type == OB_LAMP) {
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
}
}
static void rna_Scene_simplify_update_impl(Main *bmain,
@ -8034,6 +8038,14 @@ static void rna_def_scene_eevee(BlenderRNA *brna)
prop, "Tracing Method", "Select the tracing method used to find scene-ray intersections");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
prop = RNA_def_property(srna, "use_shadow_jittered_viewport", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", SCE_EEVEE_SHADOW_JITTERED_VIEWPORT);
RNA_def_property_ui_text(prop,
"Jittered Shadows (Viewport)",
"Enable jittered shadows on the viewport. (Jittered shadows are always "
"enabled for final renders)");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
/* Volumetrics */
prop = RNA_def_property(srna, "volumetric_start", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_ui_text(prop, "Start", "Start distance of the volumetric effect");

View File

@ -1610,7 +1610,7 @@ static StructRNA *rna_Operator_register(Main *bmain,
}
/* XXX, this doubles up with the operator name #29666.
* for now just remove from dir(bpy.types) */
* for now just remove from `dir(bpy.types)`. */
/* create a new operator type */
dummy_ot.rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummy_ot.idname, &RNA_Operator);
@ -1783,7 +1783,7 @@ static StructRNA *rna_MacroOperator_register(Main *bmain,
}
/* XXX, this doubles up with the operator name #29666.
* for now just remove from dir(bpy.types) */
* for now just remove from `dir(bpy.types)`. */
/* create a new operator type */
dummy_ot.rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummy_ot.idname, &RNA_Operator);

View File

@ -51,6 +51,7 @@ set(SRC
intern/MOD_fluid.cc
intern/MOD_grease_pencil_armature.cc
intern/MOD_grease_pencil_array.cc
intern/MOD_grease_pencil_build.cc
intern/MOD_grease_pencil_color.cc
intern/MOD_grease_pencil_dash.cc
intern/MOD_grease_pencil_envelope.cc

View File

@ -96,6 +96,7 @@ extern ModifierTypeInfo modifierType_GreasePencilTime;
extern ModifierTypeInfo modifierType_GreasePencilEnvelope;
extern ModifierTypeInfo modifierType_GreasePencilOutline;
extern ModifierTypeInfo modifierType_GreasePencilShrinkwrap;
extern ModifierTypeInfo modifierType_GreasePencilBuild;
/* MOD_util.cc */

View File

@ -0,0 +1,801 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "BLI_array.hh"
#include "BLI_hash.h"
#include "BLI_rand.h"
#include "BLI_sort.hh"
#include "BLI_task.h"
#include "BLT_translation.hh"
#include "BLO_read_write.hh"
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DEG_depsgraph_query.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_query.hh"
#include "BKE_modifier.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "MOD_grease_pencil_util.hh"
#include "MOD_modifiertypes.hh"
#include "MOD_ui_common.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "GEO_reorder.hh"
namespace blender {
static void init_data(ModifierData *md)
{
auto *gpmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(GreasePencilBuildModifierData), modifier);
modifier::greasepencil::init_influence_data(&gpmd->influence, false);
}
static void copy_data(const ModifierData *md, ModifierData *target, int flags)
{
const auto *omd = reinterpret_cast<const GreasePencilBuildModifierData *>(md);
auto *tomd = reinterpret_cast<GreasePencilBuildModifierData *>(target);
modifier::greasepencil::free_influence_data(&tomd->influence);
BKE_modifier_copydata_generic(md, target, flags);
modifier::greasepencil::copy_influence_data(&omd->influence, &tomd->influence, flags);
}
static void free_data(ModifierData *md)
{
auto *omd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
modifier::greasepencil::free_influence_data(&omd->influence);
}
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
{
auto *omd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
modifier::greasepencil::foreach_influence_ID_link(&omd->influence, ob, walk, user_data);
}
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
auto *mmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
if (mmd->object != nullptr) {
DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Build Modifier");
}
DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Build Modifier");
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
{
const auto *mmd = reinterpret_cast<const GreasePencilBuildModifierData *>(md);
BLO_write_struct(writer, GreasePencilBuildModifierData, mmd);
modifier::greasepencil::write_influence_data(writer, &mmd->influence);
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
modifier::greasepencil::read_influence_data(reader, &mmd->influence);
}
static Array<int> point_counts_to_keep_concurrent(const bke::CurvesGeometry &curves,
const IndexMask &selection,
const int time_alignment,
const int transition,
const float factor,
const bool clamp_points,
int &r_curves_num,
int &r_points_num)
{
const int stroke_count = curves.curves_num();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
curves.ensure_evaluated_lengths();
float max_length = 0;
for (const int stroke : curves.curves_range()) {
const float len = curves.evaluated_length_total_for_curve(stroke, false);
max_length = math::max(max_length, len);
}
float factor_to_keep = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW ? factor :
1.0f - factor;
if (clamp_points) {
r_curves_num = r_points_num = 0;
factor_to_keep = std::clamp(factor_to_keep, 0.0f, 1.0f);
}
auto get_stroke_factor = [&](const float factor, const int index) {
const float max_factor = max_length / curves.evaluated_length_total_for_curve(index, false);
if (time_alignment == MOD_GREASE_PENCIL_BUILD_TIMEALIGN_START) {
if (clamp_points) {
return std::clamp(factor * max_factor, 0.0f, 1.0f);
}
return factor * max_factor;
}
if (time_alignment == MOD_GREASE_PENCIL_BUILD_TIMEALIGN_END) {
const float min_factor = max_factor - 1.0f;
const float use_factor = factor * max_factor;
if (clamp_points) {
return std::clamp(use_factor - min_factor, 0.0f, 1.0f);
}
return use_factor - min_factor;
}
return 0.0f;
};
Array<bool> select(stroke_count);
selection.to_bools(select.as_mutable_span());
Array<int> result(stroke_count);
for (const int curve : curves.curves_range()) {
const float local_factor = select[curve] ? get_stroke_factor(factor_to_keep, curve) : 1.0f;
const int num_points = points_by_curve[curve].size() * local_factor;
result[curve] = num_points;
if (clamp_points) {
r_points_num += num_points;
if (num_points > 0) {
r_curves_num++;
}
}
}
return result;
}
static bke::CurvesGeometry build_concurrent(bke::greasepencil::Drawing &drawing,
bke::CurvesGeometry &curves,
const IndexMask &selection,
const int time_alignment,
const int transition,
const float factor,
const float factor_start,
const float factor_opacity,
const float factor_radii,
StringRefNull target_vgname)
{
int dst_curves_num, dst_points_num;
const bool has_fade = factor_start != factor;
const Array<int> point_counts_to_keep = point_counts_to_keep_concurrent(
curves, selection, time_alignment, transition, factor, true, dst_curves_num, dst_points_num);
if (dst_curves_num == 0) {
return {};
}
const Array<int> starts_per_curve = has_fade ? point_counts_to_keep_concurrent(curves,
selection,
time_alignment,
transition,
factor_start,
false,
dst_curves_num,
dst_points_num) :
Array<int>(0);
const Array<int> ends_per_curve = has_fade ? point_counts_to_keep_concurrent(curves,
selection,
time_alignment,
transition,
factor,
false,
dst_curves_num,
dst_points_num) :
Array<int>(0);
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
MutableSpan<float> opacities = drawing.opacities_for_write();
MutableSpan<float> radii = drawing.radii_for_write();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
bke::SpanAttributeWriter<float> weights = attributes.lookup_for_write_span<float>(target_vgname);
const bool is_vanishing = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
bke::CurvesGeometry dst_curves(dst_points_num, dst_curves_num);
Array<int> dst_to_src_point(dst_points_num);
Array<int> dst_to_src_curve(dst_curves_num);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
dst_offsets[0] = 0;
int next_curve = 0;
int next_point = 0;
for (const int curve : curves.curves_range()) {
if (!point_counts_to_keep[curve]) {
continue;
}
const IndexRange points = points_by_curve[curve];
dst_offsets[next_curve] = point_counts_to_keep[curve];
const int curve_size = points.size();
auto get_fade_weight = [&](const int local_index) {
const float fade_range = std::abs(ends_per_curve[curve] - starts_per_curve[curve]);
if (is_vanishing) {
const float factor_from_start = local_index - curve_size + ends_per_curve[curve];
return 1.0f - std::clamp(factor_from_start / fade_range, 0.0f, 1.0f);
}
const float factor_from_start = local_index - starts_per_curve[curve];
return std::clamp(factor_from_start / fade_range, 0.0f, 1.0f);
};
const int extra_offset = is_vanishing ? points.size() - point_counts_to_keep[curve] : 0;
for (const int stroke_point : IndexRange(point_counts_to_keep[curve])) {
const int src_point_index = points.first() + extra_offset + stroke_point;
if (has_fade) {
const float fade_weight = get_fade_weight(extra_offset + stroke_point);
opacities[src_point_index] = opacities[src_point_index] *
(1.0f - fade_weight * factor_opacity);
radii[src_point_index] = radii[src_point_index] * (1.0f - fade_weight * factor_radii);
if (!weights.span.is_empty()) {
weights.span[src_point_index] = fade_weight;
}
}
dst_to_src_point[next_point] = src_point_index;
next_point++;
}
dst_to_src_curve[next_curve] = curve;
next_curve++;
}
weights.finish();
offset_indices::accumulate_counts_to_offsets(dst_offsets);
const bke::AttributeAccessor src_attributes = curves.attributes();
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
gather_attributes(
src_attributes, bke::AttrDomain::Point, {}, {}, dst_to_src_point, dst_attributes);
gather_attributes(
src_attributes, bke::AttrDomain::Curve, {}, {}, dst_to_src_curve, dst_attributes);
dst_curves.update_curve_types();
return dst_curves;
}
static void points_info_sequential(const bke::CurvesGeometry &curves,
const IndexMask &selection,
const int transition,
const float factor,
const bool clamp_points,
int &r_curves_num,
int &r_points_num)
{
const int stroke_count = curves.curves_num();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
float factor_to_keep = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW ? factor :
(1.0f - factor);
if (clamp_points) {
factor_to_keep = std::clamp(factor_to_keep, 0.0f, 1.0f);
}
const bool is_vanishing = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
int effective_points_num = 0;
selection.foreach_index(
[&](const int index) { effective_points_num += points_by_curve[index].size(); });
const int untouched_points_num = points_by_curve.total_size() - effective_points_num;
effective_points_num *= factor_to_keep;
effective_points_num += untouched_points_num;
r_points_num = effective_points_num;
r_curves_num = 0;
Array<bool> select(stroke_count);
selection.to_bools(select.as_mutable_span());
int counted_points_num = 0;
for (const int i : curves.curves_range()) {
const int stroke = is_vanishing ? stroke_count - i - 1 : i;
if (select[stroke] && counted_points_num >= effective_points_num) {
continue;
}
counted_points_num += points_by_curve[stroke].size();
r_curves_num++;
}
}
static bke::CurvesGeometry build_sequential(bke::greasepencil::Drawing &drawing,
bke::CurvesGeometry &curves,
const IndexMask &selection,
const int transition,
const float factor,
const float factor_start,
const float factor_opacity,
const float factor_radii,
StringRefNull target_vgname)
{
const bool has_fade = factor_start != factor;
int dst_curves_num, dst_points_num;
int start_points_num, end_points_num, dummy_curves_num;
points_info_sequential(
curves, selection, transition, factor, true, dst_curves_num, dst_points_num);
if (dst_curves_num == 0) {
return {};
}
points_info_sequential(
curves, selection, transition, factor_start, false, dummy_curves_num, start_points_num);
points_info_sequential(
curves, selection, transition, factor, false, dummy_curves_num, end_points_num);
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
MutableSpan<float> opacities = drawing.opacities_for_write();
MutableSpan<float> radii = drawing.radii_for_write();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
bke::SpanAttributeWriter<float> weights = attributes.lookup_for_write_span<float>(target_vgname);
const bool is_vanishing = transition == MOD_GREASE_PENCIL_BUILD_TRANSITION_VANISH;
bke::CurvesGeometry dst_curves(dst_points_num, dst_curves_num);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
Array<int> dst_to_src_point(dst_points_num);
Array<int> dst_to_src_curve(dst_curves_num);
dst_offsets[0] = 0;
int next_curve = 1, next_point = 0;
IndexMaskMemory memory;
selection.complement(curves.curves_range(), memory).foreach_index([&](const int stroke) {
for (const int point : points_by_curve[stroke]) {
dst_to_src_point[next_point] = point;
next_point++;
}
dst_offsets[next_curve] = next_point;
next_curve++;
});
const int stroke_count = curves.curves_num();
bool done_scanning = false;
selection.foreach_index([&](const int i) {
const int stroke = is_vanishing ? stroke_count - i - 1 : i;
if (done_scanning || next_point >= dst_points_num) {
done_scanning = true;
return;
}
auto get_fade_weight = [&](const int next_point_count) {
return std::clamp(float(next_point_count - start_points_num) /
float(abs(end_points_num - start_points_num)),
0.0f,
1.0f);
};
const IndexRange points = points_by_curve[stroke];
for (const int point : points) {
const int local_index = point - points.first();
const int src_point_index = is_vanishing ? points.last() - local_index : point;
dst_to_src_point[next_point] = src_point_index;
if (has_fade) {
const float fade_weight = get_fade_weight(next_point);
opacities[src_point_index] = opacities[src_point_index] *
(1.0f - fade_weight * factor_opacity);
radii[src_point_index] = radii[src_point_index] * (1.0f - fade_weight * factor_radii);
if (!weights.span.is_empty()) {
weights.span[src_point_index] = fade_weight;
}
}
next_point++;
if (next_point >= dst_points_num) {
done_scanning = true;
break;
}
}
dst_offsets[next_curve] = next_point;
dst_to_src_curve[next_curve - 1] = i;
next_curve++;
});
weights.finish();
BLI_assert(next_curve == (dst_curves_num + 1));
BLI_assert(next_point == dst_points_num);
const bke::AttributeAccessor src_attributes = curves.attributes();
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
gather_attributes(
src_attributes, bke::AttrDomain::Point, {}, {}, dst_to_src_point, dst_attributes);
gather_attributes(
src_attributes, bke::AttrDomain::Curve, {}, {}, dst_to_src_curve, dst_attributes);
dst_curves.update_curve_types();
return dst_curves;
}
static bke::CurvesGeometry reorder_strokes(const bke::CurvesGeometry &curves,
const Span<bool> select,
const Object &object,
MutableSpan<bool> r_selection)
{
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
const float3 center = object.object_to_world().location();
struct Pair {
float value;
int index;
bool selected;
};
Array<Pair> distances(curves.curves_num());
for (const int stroke : curves.curves_range()) {
const IndexRange points = points_by_curve[stroke];
const float3 p1 = positions[points.first()];
const float3 p2 = positions[points.last()];
distances[stroke].value = math::max(math::distance(p1, center), math::distance(p2, center));
distances[stroke].index = stroke;
distances[stroke].selected = select[stroke];
}
parallel_sort(
distances.begin(), distances.end(), [](Pair &a, Pair &b) { return a.value < b.value; });
Array<int> new_order(curves.curves_num());
for (const int i : curves.curves_range()) {
new_order[i] = distances[i].index;
r_selection[i] = distances[i].selected;
}
return geometry::reorder_curves_geometry(curves, new_order.as_span(), {});
}
static float get_factor_from_draw_speed(const bke::CurvesGeometry &curves,
const float time_elapsed,
const float speed_fac,
const float max_gap)
{
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const bke::AttributeAccessor attributes = curves.attributes();
const VArray<float> init_times =
attributes.lookup_or_default<float>("init_time", bke::AttrDomain::Curve, 0.0f).varray;
const VArray<float> delta_times =
attributes.lookup_or_default<float>("delta_time", bke::AttrDomain::Point, 0.0f).varray;
Array<float> times(curves.points_num());
float current_time = 0;
float previous_init_time = init_times[0];
for (const int curve : curves.curves_range()) {
if (curve > 0) {
current_time += math::max(init_times[curve] - previous_init_time, max_gap);
previous_init_time = init_times[curve];
}
for (const int point : points_by_curve[curve]) {
current_time += delta_times[point];
times[point] = current_time;
}
}
for (const int point : curves.points_range()) {
const float limit = time_elapsed * speed_fac;
if (times[point] >= limit) {
return math::clamp(float(point) / float(curves.points_num()), 0.0f, 1.0f);
}
}
return 1.0f;
}
static float get_build_factor(const GreasePencilBuildTimeMode time_mode,
const int current_frame,
const int start_frame,
const int length,
const float percentage,
const bke::CurvesGeometry &curves,
const float scene_fps,
const float speed_fac,
const float max_gap,
const float fade)
{
switch (time_mode) {
case MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES:
return math::clamp(float(current_frame - start_frame) / length, 0.0f, 1.0f) * (1.0f + fade);
case MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE:
return percentage * (1.0f + fade);
case MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED:
return get_factor_from_draw_speed(
curves, float(current_frame) / scene_fps, speed_fac, max_gap) *
(1.0f + fade);
}
BLI_assert_unreachable();
return 0.0f;
}
static void build_drawing(const GreasePencilBuildModifierData &mmd,
const Object &ob,
bke::greasepencil::Drawing &drawing,
const bke::greasepencil::Drawing *previous_drawing,
const int current_time,
const float scene_fps)
{
bke::CurvesGeometry &curves = drawing.strokes_for_write();
if (curves.points_num() == 0) {
return;
}
IndexMaskMemory memory;
IndexMask selection = modifier::greasepencil::get_filtered_stroke_mask(
&ob, curves, mmd.influence, memory);
/* Remove a count of #prev_strokes. */
if (mmd.mode == MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE && previous_drawing != nullptr) {
const bke::CurvesGeometry &prev_curves = previous_drawing->strokes();
const int prev_strokes = prev_curves.curves_num();
const int added_strokes = curves.curves_num() - prev_strokes;
if (added_strokes > 0) {
Array<bool> work_on_select(curves.curves_num());
selection.to_bools(work_on_select.as_mutable_span());
work_on_select.as_mutable_span().take_front(prev_strokes).fill(false);
selection = IndexMask::from_bools(work_on_select, memory);
}
}
if (mmd.object) {
const int curves_num = curves.curves_num();
Array<bool> select(curves_num), reordered_select(curves_num);
selection.to_bools(select);
curves = reorder_strokes(
curves, select.as_span(), *mmd.object, reordered_select.as_mutable_span());
selection = IndexMask::from_bools(reordered_select, memory);
}
const float fade_factor = ((mmd.flag & MOD_GREASE_PENCIL_BUILD_USE_FADING) != 0) ? mmd.fade_fac :
0.0f;
float factor = get_build_factor(GreasePencilBuildTimeMode(mmd.time_mode),
current_time,
mmd.start_delay,
mmd.length,
mmd.percentage_fac,
curves,
scene_fps,
mmd.speed_fac,
mmd.speed_maxgap,
fade_factor);
float factor_start = factor - fade_factor;
if (mmd.transition != MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW) {
std::swap(factor, factor_start);
}
const float use_time_alignment = mmd.transition != MOD_GREASE_PENCIL_BUILD_TRANSITION_GROW ?
!mmd.time_alignment :
mmd.time_alignment;
switch (mmd.mode) {
default:
case MOD_GREASE_PENCIL_BUILD_MODE_SEQUENTIAL:
curves = build_sequential(drawing,
curves,
selection,
mmd.transition,
factor,
factor_start,
mmd.fade_opacity_strength,
mmd.fade_thickness_strength,
mmd.target_vgname);
break;
case MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT:
curves = build_concurrent(drawing,
curves,
selection,
use_time_alignment,
mmd.transition,
factor,
factor_start,
mmd.fade_opacity_strength,
mmd.fade_thickness_strength,
mmd.target_vgname);
break;
case MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE:
curves = build_sequential(drawing,
curves,
selection,
mmd.transition,
factor,
factor_start,
mmd.fade_opacity_strength,
mmd.fade_thickness_strength,
mmd.target_vgname);
break;
}
drawing.tag_topology_changed();
}
static void modify_geometry_set(ModifierData *md,
const ModifierEvalContext *ctx,
blender::bke::GeometrySet *geometry_set)
{
const auto *mmd = reinterpret_cast<GreasePencilBuildModifierData *>(md);
if (!geometry_set->has_grease_pencil()) {
return;
}
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
const int eval_frame = grease_pencil.runtime->eval_frame;
IndexMaskMemory mask_memory;
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
grease_pencil, mmd->influence, mask_memory);
const Vector<modifier::greasepencil::LayerDrawingInfo> drawing_infos =
modifier::greasepencil::get_drawing_infos_by_layer(grease_pencil, layer_mask, eval_frame);
if (mmd->flag & MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME) {
if (eval_frame < mmd->start_frame || eval_frame > mmd->end_frame) {
return;
}
}
const Scene &scene = *DEG_get_evaluated_scene(ctx->depsgraph);
const float scene_fps = float(scene.r.frs_sec) / scene.r.frs_sec_base;
const Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
threading::parallel_for_each(
drawing_infos, [&](modifier::greasepencil::LayerDrawingInfo drawing_info) {
const bke::greasepencil::Drawing *prev_drawing = grease_pencil.get_drawing_at(
*layers[drawing_info.layer_index], eval_frame - 1);
build_drawing(
*mmd, *ctx->object, *drawing_info.drawing, prev_drawing, eval_frame, scene_fps);
});
}
static void panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA ob_ptr;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
const GreasePencilBuildMode mode = GreasePencilBuildMode(RNA_enum_get(ptr, "mode"));
GreasePencilBuildTimeMode time_mode = GreasePencilBuildTimeMode(RNA_enum_get(ptr, "time_mode"));
uiLayoutSetPropSep(layout, true);
/* First: Build mode and build settings. */
uiItemR(layout, ptr, "mode", UI_ITEM_NONE, nullptr, ICON_NONE);
if (mode == MOD_GREASE_PENCIL_BUILD_MODE_SEQUENTIAL) {
uiItemR(layout, ptr, "transition", UI_ITEM_NONE, nullptr, ICON_NONE);
}
if (mode == MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT) {
/* Concurrent mode doesn't support MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED, so unset it. */
if (time_mode == MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED) {
RNA_enum_set(ptr, "time_mode", MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES);
time_mode = MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES;
}
uiItemR(layout, ptr, "transition", UI_ITEM_NONE, nullptr, ICON_NONE);
}
uiItemS(layout);
/* Second: Time mode and time settings. */
uiItemR(layout, ptr, "time_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
if (mode == MOD_GREASE_PENCIL_BUILD_MODE_CONCURRENT) {
uiItemR(layout, ptr, "concurrent_time_alignment", UI_ITEM_NONE, nullptr, ICON_NONE);
}
switch (time_mode) {
case MOD_GREASE_PENCIL_BUILD_TIMEMODE_DRAWSPEED:
uiItemR(layout, ptr, "speed_factor", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(layout, ptr, "speed_maxgap", UI_ITEM_NONE, nullptr, ICON_NONE);
break;
case MOD_GREASE_PENCIL_BUILD_TIMEMODE_FRAMES:
uiItemR(layout, ptr, "length", UI_ITEM_NONE, IFACE_("Frames"), ICON_NONE);
if (mode != MOD_GREASE_PENCIL_BUILD_MODE_ADDITIVE) {
uiItemR(layout, ptr, "start_delay", UI_ITEM_NONE, nullptr, ICON_NONE);
}
break;
case MOD_GREASE_PENCIL_BUILD_TIMEMODE_PERCENTAGE:
uiItemR(layout, ptr, "percentage_factor", UI_ITEM_NONE, nullptr, ICON_NONE);
break;
default:
break;
}
uiItemS(layout);
uiItemR(layout, ptr, "object", UI_ITEM_NONE, nullptr, ICON_NONE);
if (uiLayout *panel = uiLayoutPanelProp(
C, layout, ptr, "open_frame_range_panel", "Effective Range"))
{
uiLayoutSetPropSep(panel, true);
uiItemR(
panel, ptr, "use_restrict_frame_range", UI_ITEM_NONE, IFACE_("Custom Range"), ICON_NONE);
const bool active = RNA_boolean_get(ptr, "use_restrict_frame_range");
uiLayout *col = uiLayoutColumn(panel, false);
uiLayoutSetActive(col, active);
uiItemR(col, ptr, "frame_start", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
uiItemR(col, ptr, "frame_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
}
if (uiLayout *panel = uiLayoutPanelProp(C, layout, ptr, "open_fading_panel", "Fading")) {
uiLayoutSetPropSep(panel, true);
uiItemR(panel, ptr, "use_fading", UI_ITEM_NONE, IFACE_("Fade"), ICON_NONE);
const bool active = RNA_boolean_get(ptr, "use_fading");
uiLayout *col = uiLayoutColumn(panel, false);
uiLayoutSetActive(col, active);
uiItemR(col, ptr, "fade_factor", UI_ITEM_NONE, IFACE_("Factor"), ICON_NONE);
uiLayout *subcol = uiLayoutColumn(col, true);
uiItemR(subcol, ptr, "fade_thickness_strength", UI_ITEM_NONE, IFACE_("Thickness"), ICON_NONE);
uiItemR(subcol, ptr, "fade_opacity_strength", UI_ITEM_NONE, IFACE_("Opacity"), ICON_NONE);
uiItemPointerR(col,
ptr,
"target_vertex_group",
&ob_ptr,
"vertex_groups",
IFACE_("Weight Output"),
ICON_NONE);
}
if (uiLayout *influence_panel = uiLayoutPanelProp(
C, layout, ptr, "open_influence_panel", "Influence"))
{
modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr);
modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr);
}
modifier_panel_end(layout, ptr);
}
static void panel_register(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_GreasePencilBuild, panel_draw);
}
} // namespace blender
ModifierTypeInfo modifierType_GreasePencilBuild = {
/*idname*/ "GreasePencilBuildModifier",
/*name*/ N_("Build"),
/*struct_name*/ "GreasePencilBuildModifierData",
/*struct_size*/ sizeof(GreasePencilBuildModifierData),
/*srna*/ &RNA_GreasePencilBuildModifier,
/*type*/ ModifierTypeType::Nonconstructive,
/*flags*/
eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_EnableInEditmode |
eModifierTypeFlag_SupportsEditmode,
/*icon*/ ICON_MOD_LENGTH,
/*copy_data*/ blender::copy_data,
/*deform_verts*/ nullptr,
/*deform_matrices*/ nullptr,
/*deform_verts_EM*/ nullptr,
/*deform_matrices_EM*/ nullptr,
/*modify_mesh*/ nullptr,
/*modify_geometry_set*/ blender::modify_geometry_set,
/*init_data*/ blender::init_data,
/*required_data_mask*/ nullptr,
/*free_data*/ blender::free_data,
/*is_disabled*/ nullptr,
/*update_depsgraph*/ blender::update_depsgraph,
/*depends_on_time*/ nullptr,
/*depends_on_normals*/ nullptr,
/*foreach_ID_link*/ blender::foreach_ID_link,
/*foreach_tex_link*/ nullptr,
/*free_runtime_data*/ nullptr,
/*panel_register*/ blender::panel_register,
/*blend_write*/ blender::blend_write,
/*blend_read*/ blender::blend_read,
};

View File

@ -287,5 +287,6 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(GreasePencilEnvelope);
INIT_TYPE(GreasePencilOutline);
INIT_TYPE(GreasePencilShrinkwrap);
INIT_TYPE(GreasePencilBuild);
#undef INIT_TYPE
}

View File

@ -171,6 +171,8 @@ class SocketDeclaration : public ItemDeclaration {
/** Defined by whether the socket is part of the node's input or
* output socket declaration list. Included here for convenience. */
eNodeSocketInOut in_out;
/** Socket type that corresponds to this socket declaration. */
eNodeSocketDatatype socket_type;
bool hide_label = false;
bool hide_value = false;
bool compact = false;
@ -682,6 +684,7 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef
socket_decl->name = name;
socket_decl->identifier = identifier_in.is_empty() ? name : identifier_in;
socket_decl->in_out = SOCK_IN;
socket_decl->socket_type = DeclType::static_socket_type;
socket_decl_builder->index_ = declaration_.inputs.append_and_get_index(socket_decl.get());
declaration_.items.append(std::move(socket_decl));
input_socket_builders_.append(&*socket_decl_builder);
@ -693,6 +696,7 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef
socket_decl->name = name;
socket_decl->identifier = identifier_out.is_empty() ? name : identifier_out;
socket_decl->in_out = SOCK_OUT;
socket_decl->socket_type = DeclType::static_socket_type;
socket_decl_builder->index_ = declaration_.outputs.append_and_get_index(socket_decl.get());
declaration_.items.append(std::move(socket_decl));
output_socket_builders_.append(&*socket_decl_builder);

View File

@ -30,5 +30,6 @@ namespace blender::nodes {
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node);
bool socket_type_supports_fields(eNodeSocketDatatype socket_type);
bool socket_type_supports_grids(eNodeSocketDatatype socket_type);
} // namespace blender::nodes

View File

@ -18,6 +18,8 @@ class FloatBuilder;
class Float : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_FLOAT;
float default_value = 0.0f;
float soft_min_value = -FLT_MAX;
float soft_max_value = FLT_MAX;
@ -45,6 +47,8 @@ class IntBuilder;
class Int : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_INT;
int default_value = 0;
int soft_min_value = INT32_MIN;
int soft_max_value = INT32_MAX;
@ -72,6 +76,8 @@ class VectorBuilder;
class Vector : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_VECTOR;
float3 default_value = {0, 0, 0};
float soft_min_value = -FLT_MAX;
float soft_max_value = FLT_MAX;
@ -100,6 +106,8 @@ class BoolBuilder;
class Bool : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_BOOLEAN;
bool default_value = false;
friend BoolBuilder;
@ -120,6 +128,8 @@ class ColorBuilder;
class Color : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_RGBA;
ColorGeometry4f default_value{0.8f, 0.8f, 0.8f, 1.0f};
friend ColorBuilder;
@ -141,6 +151,8 @@ class RotationBuilder;
class Rotation : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_ROTATION;
math::EulerXYZ default_value;
friend RotationBuilder;
@ -162,6 +174,8 @@ class MatrixBuilder;
class Matrix : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_MATRIX;
friend MatrixBuilder;
using Builder = MatrixBuilder;
@ -178,6 +192,8 @@ class StringBuilder;
class String : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_STRING;
std::string default_value;
friend StringBuilder;
@ -199,6 +215,8 @@ class MenuBuilder;
class Menu : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_MENU;
int32_t default_value;
friend MenuBuilder;
@ -237,6 +255,8 @@ class IDSocketDeclaration : public SocketDeclaration {
class Object : public IDSocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_OBJECT;
using Builder = SocketDeclarationBuilder<Object>;
Object();
@ -244,6 +264,8 @@ class Object : public IDSocketDeclaration {
class Material : public IDSocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_MATERIAL;
using Builder = SocketDeclarationBuilder<Material>;
Material();
@ -251,6 +273,8 @@ class Material : public IDSocketDeclaration {
class Collection : public IDSocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_COLLECTION;
using Builder = SocketDeclarationBuilder<Collection>;
Collection();
@ -258,6 +282,8 @@ class Collection : public IDSocketDeclaration {
class Texture : public IDSocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_TEXTURE;
using Builder = SocketDeclarationBuilder<Texture>;
Texture();
@ -265,6 +291,8 @@ class Texture : public IDSocketDeclaration {
class Image : public IDSocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_IMAGE;
using Builder = SocketDeclarationBuilder<Image>;
Image();
@ -274,6 +302,8 @@ class ShaderBuilder;
class Shader : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_SHADER;
friend ShaderBuilder;
using Builder = ShaderBuilder;
@ -292,6 +322,8 @@ class Extend : public SocketDeclaration {
friend ExtendBuilder;
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_CUSTOM;
using Builder = ExtendBuilder;
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
@ -304,6 +336,8 @@ class ExtendBuilder : public SocketDeclarationBuilder<Extend> {};
class Custom : public SocketDeclaration {
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_CUSTOM;
const char *idname_;
std::function<void(bNode &node, bNodeSocket &socket, const char *data_path)> init_socket_fn;

View File

@ -21,6 +21,8 @@ class Geometry : public SocketDeclaration {
friend GeometryBuilder;
public:
static constexpr eNodeSocketDatatype static_socket_type = SOCK_GEOMETRY;
using Builder = GeometryBuilder;
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;

View File

@ -10,6 +10,7 @@
#include "BKE_node.hh"
#include "NOD_rna_define.hh"
#include "NOD_socket.hh"
#include "NOD_socket_search_link.hh"
#include "RNA_enum_types.hh"
@ -100,17 +101,12 @@ const EnumPropertyItem *domain_without_corner_experimental_grease_pencil_version
} // namespace enums
bool grid_type_supported(const eCustomDataType data_type)
bool custom_data_type_supports_grids(const eCustomDataType data_type)
{
return ELEM(data_type, CD_PROP_FLOAT, CD_PROP_FLOAT3);
}
bool grid_type_supported(eNodeSocketDatatype socket_type)
{
if (const std::optional<eCustomDataType> data_type = bke::socket_type_to_custom_data_type(
socket_type))
if (const std::optional<eNodeSocketDatatype> socket_type = bke::custom_data_type_to_socket_type(
data_type))
{
return grid_type_supported(*data_type);
return socket_type_supports_grids(*socket_type);
}
return false;
}
@ -123,7 +119,7 @@ const EnumPropertyItem *grid_custom_data_type_items_filter_fn(bContext * /*C*/,
*r_free = true;
return enum_items_filter(rna_enum_attribute_type_items,
[](const EnumPropertyItem &item) -> bool {
return grid_type_supported(eCustomDataType(item.value));
return custom_data_type_supports_grids(eCustomDataType(item.value));
});
}
@ -135,7 +131,7 @@ const EnumPropertyItem *grid_socket_type_items_filter_fn(bContext * /*C*/,
*r_free = true;
return enum_items_filter(rna_enum_node_socket_data_type_items,
[](const EnumPropertyItem &item) -> bool {
return grid_type_supported(eNodeSocketDatatype(item.value));
return socket_type_supports_grids(eNodeSocketDatatype(item.value));
});
}

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