WIP: Mesh: Improve and simplify modifier evaluation logic #119968

Draft
Hans Goudey wants to merge 33 commits from HooglyBoogly/blender:fix-modifier-eval-geometry-set-improve into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
125 changed files with 2458 additions and 591 deletions
Showing only changes of commit 04fbb2ffd3 - Show all commits

View File

@ -30,7 +30,7 @@ CHECKER_EXCLUDE_SOURCE_FILES = set(os.path.join(*f.split("/")) for f in (
"source/blender/draw/engines/eevee_next/eevee_lut.cc",
))
CHECKER_ARGS = [
CHECKER_ARGS = (
# Speed up execution.
# As Blender has many defines, the total number of configurations is large making execution unreasonably slow.
# This could be increased but do so with care.
@ -39,9 +39,15 @@ CHECKER_ARGS = [
# Enable this when includes are missing.
# "--check-config",
# This is slower, for a comprehensive output it is needed.
"--check-level=exhaustive",
# Shows many pedantic issues, some are quite useful.
"--enable=all",
# Generates many warnings, CPPCHECK known about system includes without resolving them.
"--suppress=missingIncludeSystem",
# Also shows useful messages, even if some are false-positives.
"--inconclusive",
@ -50,7 +56,15 @@ CHECKER_ARGS = [
*(() if USE_VERBOSE else ("--quiet",))
# NOTE: `--cppcheck-build-dir=<dir>` is added later as a temporary directory.
]
)
CHECKER_ARGS_C = (
"--std=c11",
)
CHECKER_ARGS_CXX = (
"--std=c++17",
)
def source_info_filter(
@ -74,22 +88,50 @@ def source_info_filter(
return source_info_result
def cppcheck() -> None:
def cppcheck(temp_dir: str) -> None:
temp_build_dir = os.path.join(temp_dir, "build")
temp_source_dir = os.path.join(temp_dir, "source")
del temp_dir
os.mkdir(temp_build_dir)
os.mkdir(temp_source_dir)
source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX)
source_defines = project_source_info.build_defines_as_args()
cppcheck_compiler_h = os.path.join(temp_source_dir, "cppcheck_compiler.h")
with open(cppcheck_compiler_h, "w", encoding="utf-8") as fh:
fh.write(project_source_info.build_defines_as_source())
# Add additional defines.
fh.write("\n")
# Python's `pyport.h` errors without this.
fh.write("#define UCHAR_MAX 255\n")
# `intern/atomic/intern/atomic_ops_utils.h` errors with `Cannot find int size` without this.
fh.write("#define UINT_MAX 0xFFFFFFFF\n")
# Apply exclusion.
source_info = source_info_filter(source_info)
check_commands = []
for c, inc_dirs, defs in source_info:
if c.endswith(".c"):
checker_args_extra = CHECKER_ARGS_C
else:
checker_args_extra = CHECKER_ARGS_CXX
cmd = (
[CHECKER_BIN] +
CHECKER_ARGS +
[c] +
[("-I%s" % i) for i in inc_dirs] +
[("-D%s" % d) for d in defs] +
source_defines
CHECKER_BIN,
*CHECKER_ARGS,
*checker_args_extra,
"--cppcheck-build-dir=" + temp_build_dir,
"--include=" + cppcheck_compiler_h,
# NOTE: for some reason failing to include this crease a large number of syntax errors
# from `intern/guardedalloc/MEM_guardedalloc.h`. Include directly to resolve.
"--include=" + os.path.join(
project_source_info.SOURCE_DIR, "source", "blender", "blenlib", "BLI_compiler_attrs.h",
),
c,
*[("-I%s" % i) for i in inc_dirs],
*[("-D%s" % d) for d in defs],
)
check_commands.append((c, cmd))
@ -119,8 +161,7 @@ def cppcheck() -> None:
def main() -> None:
with tempfile.TemporaryDirectory() as temp_dir:
CHECKER_ARGS.append("--cppcheck-build-dir=" + temp_dir)
cppcheck()
cppcheck(temp_dir)
if __name__ == "__main__":

View File

@ -24,12 +24,12 @@ from typing import (
Any,
Callable,
Generator,
IO,
List,
Optional,
Sequence,
Tuple,
Union,
cast,
)
import shlex
@ -120,12 +120,13 @@ def makefile_log() -> List[str]:
time.sleep(1)
# We know this is always true based on the input arguments to `Popen`.
stdout: IO[bytes] = process.stdout # type: ignore
assert process.stdout is not None
stdout: IO[bytes] = process.stdout
out = stdout.read()
stdout.close()
print("done!", len(out), "bytes")
return cast(List[str], out.decode("utf-8", errors="ignore").split("\n"))
return out.decode("utf-8", errors="ignore").split("\n")
def build_info(
@ -211,9 +212,10 @@ def build_defines_as_source() -> str:
)
# We know this is always true based on the input arguments to `Popen`.
stdout: IO[bytes] = process.stdout # type: ignore
assert process.stdout is not None
stdout: IO[bytes] = process.stdout
return cast(str, stdout.read().strip().decode('ascii'))
return stdout.read().strip().decode('ascii')
def build_defines_as_args() -> List[str]:

View File

@ -33,7 +33,7 @@ def main() -> None:
blender_srcdir = Path(__file__).absolute().parent.parent.parent
cli_parser = argparse.ArgumentParser(
description=f"Create a tarball of the Blender sources, optionally including sources of dependencies.",
description="Create a tarball of the Blender sources, optionally including sources of dependencies.",
epilog="This script is intended to be run by `make source_archive_complete`.",
)
cli_parser.add_argument(

View File

@ -393,7 +393,7 @@ def floating_checkout_add_origin_if_needed(
upstream_url = make_utils.git_get_remote_url(args.git_command, "upstream")
call((args.git_command, "remote", "rename", "upstream", "origin"))
make_utils.git_set_config(args.git_command, f"remote.origin.url", origin_external_url)
make_utils.git_set_config(args.git_command, "remote.origin.url", origin_external_url)
call((args.git_command, "remote", "add", "upstream", upstream_url))
finally:

View File

@ -5,6 +5,17 @@
import time
def has_module(module_name):
found = False
try:
__import__(module_name)
found = True
except ModuleNotFoundError as ex:
if ex.name != module_name:
raise ex
return found
# These are substituted when this file is copied to the build directory.
BLENDER_VERSION_STRING = "${BLENDER_VERSION_STRING}"
BLENDER_VERSION_DOTS = "${BLENDER_VERSION_DOTS}"
@ -27,6 +38,16 @@ else:
extensions = ["sphinx.ext.intersphinx"]
intersphinx_mapping = {"blender_manual": ("https://docs.blender.org/manual/en/dev/", None)}
# Provides copy button next to code-blocks (nice to have but not essential).
if has_module("sphinx_copybutton"):
extensions.append("sphinx_copybutton")
# Exclude line numbers, prompts, and console text.
copybutton_exclude = ".linenos, .gp, .go"
project = "Blender %s Python API" % BLENDER_VERSION_STRING
root_doc = "index"
copyright = "Blender Authors"
@ -48,14 +69,8 @@ html_title = "Blender Python API"
# The fallback to a built-in theme when `furo` is not found.
html_theme = "default"
try:
import furo
if has_module("furo"):
html_theme = "furo"
del furo
except ModuleNotFoundError:
pass
if html_theme == "furo":
html_theme_options = {
"light_css_variables": {
"color-brand-primary": "#265787",

View File

@ -17,10 +17,10 @@ animation or modifiers into account:
When is used on evaluated object all modifiers are taken into account.
.. note:: The result mesh is owned by the object. It can be freed by calling `object.to_mesh_clear()`.
.. note:: The result mesh is owned by the object. It can be freed by calling :meth:`~Object.to_mesh_clear`.
.. note::
The result mesh must be treated as temporary, and cannot be referenced from objects in the main
database. If the mesh intended to be used in a persistent manner use bpy.data.meshes.new_from_object()
database. If the mesh intended to be used in a persistent manner use :meth:`~BlendDataMeshes.new_from_object`
instead.
.. note:: If object does not have geometry (i.e. camera) the functions returns None.
"""

View File

@ -15,7 +15,7 @@ If the object is a text object. The text will be converted into a 3D curve and r
never applied on text objects and apply_modifiers will be ignored. If the object is neither a curve nor
a text object, an error will be reported.
.. note:: The resulting curve is owned by the object. It can be freed by calling `object.to_curve_clear()`.
.. note:: The resulting curve is owned by the object. It can be freed by calling :meth:`~Object.to_curve_clear`.
.. note::
The resulting curve must be treated as temporary, and cannot be referenced from objects in the main
database.

View File

@ -11,3 +11,6 @@ requests==2.31.0
# Without this theme, the default theme will be used.
furo==2024.1.29
sphinx-basic-ng==1.0.0b2
# Show a copy button (convenience only).
sphinx-copybutton==0.5.2

View File

@ -124,13 +124,12 @@ void PulseAudioDevice::playing(bool playing)
AUD_pa_threaded_mainloop_lock(m_mainloop);
AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr);
AUD_pa_threaded_mainloop_unlock(m_mainloop);
if(!playing)
{
AUD_pa_stream_flush(m_stream, nullptr, nullptr);
m_clear = true;
}
AUD_pa_threaded_mainloop_unlock(m_mainloop);
}
PulseAudioDevice::PulseAudioDevice(const std::string &name, DeviceSpecs specs, int buffersize) :

View File

@ -18,9 +18,8 @@
#include "util/texture.h"
#include "util/types.h"
/* On x86_64, versions of glibc < 2.16 have an issue where expf is
* much slower than the double version. This was fixed in glibc 2.16.
*/
/* On x86_64, versions of GLIBC < 2.16 have an issue where `expf` is
* much slower than the double version. This was fixed in GLIBC 2.16. */
#if !defined(__KERNEL_GPU__) && defined(__x86_64__) && defined(__x86_64__) && \
defined(__GNU_LIBRARY__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__) && \
(__GLIBC__ <= 2 && __GLIBC_MINOR__ < 16)

View File

@ -38,7 +38,7 @@
#define ccl_global
#define ccl_always_inline __attribute__((always_inline))
#define ccl_device_inline inline
#define ccl_noinline __attribute__((noinline))
#define ccl_noinline
#define ccl_inline_constant const constexpr
#define ccl_device_constant static constexpr
#define ccl_static_constexpr static constexpr

View File

@ -7,76 +7,82 @@
#define vector3 point
float safe_noise(float p)
float safe_noise(float co)
{
float f = noise("noise", p);
if (isinf(f)) {
return 0.5;
}
return f;
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
float p = fmod(co, 100000.0);
return noise("noise", p);
}
float safe_noise(vector2 p)
float safe_noise(vector2 co)
{
float f = noise("noise", p.x, p.y);
if (isinf(f)) {
return 0.5;
}
return f;
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector2 p = fmod(co, 100000.0);
return noise("noise", p.x, p.y);
}
float safe_noise(vector3 p)
float safe_noise(vector3 co)
{
float f = noise("noise", p);
if (isinf(f)) {
return 0.5;
}
return f;
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector3 p = fmod(co, 100000.0);
return noise("noise", p);
}
float safe_noise(vector4 p)
float safe_noise(vector4 co)
{
float f = noise("noise", vector3(p.x, p.y, p.z), p.w);
if (isinf(f)) {
return 0.5;
}
return f;
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector4 p = fmod(co, 100000.0);
return noise("noise", vector3(p.x, p.y, p.z), p.w);
}
float safe_snoise(float p)
float safe_snoise(float co)
{
float f = noise("snoise", p);
if (isinf(f)) {
return 0.0;
}
return f;
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
float p = fmod(co, 100000.0);
return noise("snoise", p);
}
float safe_snoise(vector2 p)
float safe_snoise(vector2 co)
{
float f = noise("snoise", p.x, p.y);
if (isinf(f)) {
return 0.0;
}
return f;
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector2 p = fmod(co, 100000.0);
return noise("snoise", p.x, p.y);
}
float safe_snoise(vector3 p)
float safe_snoise(vector3 co)
{
float f = noise("snoise", p);
if (isinf(f)) {
return 0.0;
}
return f;
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector3 p = fmod(co, 100000.0);
return noise("snoise", p);
}
float safe_snoise(vector4 p)
float safe_snoise(vector4 co)
{
float f = noise("snoise", vector3(p.x, p.y, p.z), p.w);
if (isinf(f)) {
return 0.0;
}
return f;
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector4 p = fmod(co, 100000.0);
return noise("snoise", vector3(p.x, p.y, p.z), p.w);
}
#define NOISE_FBM(T) \

View File

@ -684,7 +684,12 @@ ccl_device_inline float noise_scale4(float result)
ccl_device_inline float snoise_1d(float p)
{
return noise_scale1(ensure_finite(perlin_1d(p)));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
/* The 1D variant of fmod is called fmodf. */
p = fmodf(p, 100000.0f);
return noise_scale1(perlin_1d(p));
}
ccl_device_inline float noise_1d(float p)
@ -694,7 +699,12 @@ ccl_device_inline float noise_1d(float p)
ccl_device_inline float snoise_2d(float2 p)
{
return noise_scale2(ensure_finite(perlin_2d(p.x, p.y)));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f);
return noise_scale2(perlin_2d(p.x, p.y));
}
ccl_device_inline float noise_2d(float2 p)
@ -704,7 +714,12 @@ ccl_device_inline float noise_2d(float2 p)
ccl_device_inline float snoise_3d(float3 p)
{
return noise_scale3(ensure_finite(perlin_3d(p.x, p.y, p.z)));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f);
return noise_scale3(perlin_3d(p.x, p.y, p.z));
}
ccl_device_inline float noise_3d(float3 p)
@ -714,7 +729,12 @@ ccl_device_inline float noise_3d(float3 p)
ccl_device_inline float snoise_4d(float4 p)
{
return noise_scale4(ensure_finite(perlin_4d(p.x, p.y, p.z, p.w)));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f);
return noise_scale4(perlin_4d(p.x, p.y, p.z, p.w));
}
ccl_device_inline float noise_4d(float4 p)

View File

@ -275,8 +275,8 @@ ccl_device_inline float4 improve_5throot_solution_sse2(const float4 &old_result,
/* Calculate powf(x, 2.4). Working domain: 1e-10 < x < 1e+10 */
ccl_device_inline float4 fastpow24_sse2(const float4 &arg)
{
/* max, avg and |avg| errors were calculated in gcc without FMA instructions
* The final precision should be better than powf in glibc */
/* `max`, `avg` and |avg| errors were calculated in GCC without FMA instructions.
* The final precision should be better than `powf` in GLIBC. */
/* Calculate x^4/5, coefficient 0.994 was constructed manually to minimize avg error */
/* 0x3F4CCCCD = 4/5 */

View File

@ -198,6 +198,11 @@ ccl_device_inline float2 clamp(const float2 a, const float2 mn, const float2 mx)
return min(max(a, mn), mx);
}
ccl_device_inline float2 fmod(const float2 a, const float b)
{
return make_float2(fmodf(a.x, b), fmodf(a.y, b));
}
ccl_device_inline float2 fabs(const float2 a)
{
return make_float2(fabsf(a.x), fabsf(a.y));

View File

@ -309,6 +309,11 @@ ccl_device_inline float3 fabs(const float3 a)
# endif
}
ccl_device_inline float3 fmod(const float3 a, const float b)
{
return make_float3(fmodf(a.x, b), fmodf(a.y, b), fmodf(a.z, b));
}
ccl_device_inline float3 sqrt(const float3 a)
{
# ifdef __KERNEL_SSE__

View File

@ -465,6 +465,11 @@ ccl_device_inline float4 fabs(const float4 a)
# endif
}
ccl_device_inline float4 fmod(const float4 a, const float b)
{
return make_float4(fmodf(a.x, b), fmodf(a.y, b), fmodf(a.z, b), fmodf(a.w, b));
}
ccl_device_inline float4 floor(const float4 a)
{
# ifdef __KERNEL_SSE__

View File

@ -14,7 +14,7 @@ CCL_NAMESPACE_BEGIN
thread::thread(function<void()> run_cb) : run_cb_(run_cb), joined_(false)
{
#if defined(__APPLE__) || defined(__linux__) && !defined(__GLIBC__)
/* Set the stack size to 2MB to match glibc. The default 512KB on macOS is
/* Set the stack size to 2MB to match GLIBC. The default 512KB on macOS is
* too small for Embree, and consistent stack size also makes things more
* predictable in general. */
pthread_attr_t attribute;

View File

@ -427,7 +427,7 @@ struct GWL_Cursor {
/**
* The name of the theme (set by an environment variable).
* When disabled, leave as an empty string and the default theme will be used.
* When disabled, leave as an empty string and pass in nullptr to use the default theme.
*/
std::string theme_name;
/**
@ -2507,7 +2507,9 @@ static const wl_cursor *gwl_seat_cursor_find_from_shape(GWL_Seat *seat,
if (!cursor->wl.theme) {
/* The cursor wl_surface hasn't entered an output yet. Initialize theme with scale 1. */
cursor->wl.theme = wl_cursor_theme_load(
cursor->theme_name.c_str(), cursor->theme_size, seat->system->wl_shm_get());
(cursor->theme_name.empty() ? nullptr : cursor->theme_name.c_str()),
cursor->theme_size,
seat->system->wl_shm_get());
}
if (cursor->wl.theme) {
@ -3521,7 +3523,9 @@ static bool update_cursor_scale(GWL_Cursor &cursor,
}
wl_cursor_theme_destroy(cursor.wl.theme);
cursor.wl.theme = wl_cursor_theme_load(
cursor.theme_name.c_str(), scale * cursor.theme_size, shm);
(cursor.theme_name.empty() ? nullptr : cursor.theme_name.c_str()),
scale * cursor.theme_size,
shm);
if (cursor.wl.theme_cursor) {
cursor.wl.theme_cursor = wl_cursor_theme_get_cursor(cursor.wl.theme,
cursor.wl.theme_cursor_name);

View File

@ -84,7 +84,7 @@ def _kmi_properties_to_lines_recursive(level, properties, lines):
lines_test = []
_kmi_properties_to_lines_recursive(level + 2, value, lines_test)
if lines_test:
lines.append(f"(")
lines.append("(")
lines.append(f"\"{pname}\",\n")
lines.append(f"{indent(level + 3)}" "[")
lines.extend(lines_test)
@ -172,19 +172,19 @@ def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False):
fw("},\n")
fw(f"{indent(2)}" "{")
is_modal = km.is_modal
fw(f"\"items\":\n")
fw("\"items\":\n")
fw(f"{indent(3)}[")
for kmi in km.keymap_items:
if is_modal:
kmi_id = kmi.propvalue
else:
kmi_id = kmi.idname
fw(f"(")
fw("(")
kmi_args = kmi_args_as_data(kmi)
kmi_data = _kmi_attrs_or_none(4, kmi)
fw(f"\"{kmi_id:s}\"")
if kmi_data is None:
fw(f", ")
fw(", ")
else:
fw(",\n" f"{indent(5)}")

View File

@ -108,6 +108,7 @@ class OBJECT_MT_modifier_add_edit(ModifierAddMenu, Menu):
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_MIX')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_PROXIMITY')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_TEXTURE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_TIME')
self.operator_modifier_add(layout, 'GREASE_PENCIL_VERTEX_WEIGHT_PROXIMITY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_VERTEX_WEIGHT_ANGLE')

View File

@ -249,14 +249,13 @@ void *CustomData_add_layer(CustomData *data,
/**
* Adds a layer of the given type to the #CustomData object. The new layer takes ownership of the
* passed in `layer_data`. If a #ImplicitSharingInfoHandle is passed in, its user count is
* increased.
* passed in `layer_data`. If a #ImplicitSharingInfo is passed in, its user count is increased.
*/
const void *CustomData_add_layer_with_data(CustomData *data,
eCustomDataType type,
void *layer_data,
int totelem,
const ImplicitSharingInfoHandle *sharing_info);
const blender::ImplicitSharingInfo *sharing_info);
/**
* Same as above but accepts a name.
@ -272,7 +271,7 @@ const void *CustomData_add_layer_named_with_data(CustomData *data,
void *layer_data,
int totelem,
blender::StringRef name,
const ImplicitSharingInfoHandle *sharing_info);
const blender::ImplicitSharingInfo *sharing_info);
void *CustomData_add_layer_anonymous(CustomData *data,
eCustomDataType type,
@ -285,7 +284,7 @@ const void *CustomData_add_layer_anonymous_with_data(
const AnonymousAttributeIDHandle *anonymous_id,
int totelem,
void *layer_data,
const ImplicitSharingInfoHandle *sharing_info);
const blender::ImplicitSharingInfo *sharing_info);
/**
* Frees the active or first data layer with the give type.

View File

@ -76,12 +76,12 @@ class Drawing : public ::GreasePencilDrawing {
void tag_positions_changed();
void tag_topology_changed();
/*
/**
* Returns the matrices that transform from a 3D point in layer-space to a 2D point in
* texture-space.
*/
Span<float4x2> texture_matrices() const;
/*
/**
* Sets the matrices the that transform from a 3D point in layer-space to a 2D point in
* texture-space
*/

View File

@ -1359,8 +1359,6 @@ bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Objec
return false;
}
const int id_create_flag = (collection->id.tag & LIB_TAG_NO_MAIN) ? LIB_ID_CREATE_NO_MAIN : 0;
/* Only case where this pointer can be nullptr is when scene itself is linked, this case should
* never be reached. */
BLI_assert(collection != nullptr);
@ -1368,6 +1366,7 @@ bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Objec
return false;
}
const int id_create_flag = (collection->id.tag & LIB_TAG_NO_MAIN) ? LIB_ID_CREATE_NO_MAIN : 0;
if (!collection_object_add(bmain, collection, ob, nullptr, id_create_flag, true)) {
return false;
}

View File

@ -1754,8 +1754,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr},
/* 18: CD_TANGENT */
{sizeof(float[4][4]),
alignof(float[4][4]),
{sizeof(float[4]),
alignof(float[4]),
"",
0,
N_("Tangent"),

View File

@ -700,9 +700,9 @@ bool get_effector_data(EffectorCache *eff,
else if (eff->pd && eff->pd->shape == PFIELD_SHAPE_POINTS) {
/* TODO: hair and points object support */
const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(eff->ob);
const blender::Span<blender::float3> positions = mesh_eval->vert_positions();
const blender::Span<blender::float3> vert_normals = mesh_eval->vert_normals();
if (mesh_eval != nullptr) {
const blender::Span<blender::float3> positions = mesh_eval->vert_positions();
const blender::Span<blender::float3> vert_normals = mesh_eval->vert_normals();
copy_v3_v3(efd->loc, positions[*efd->index]);
copy_v3_v3(efd->nor, vert_normals[*efd->index]);

View File

@ -1028,7 +1028,7 @@ void BKE_gpencil_stroke_copy_settings(const bGPDstroke *gps_src, bGPDstroke *gps
copy_v2_v2_short(gps_dst->caps, gps_src->caps);
gps_dst->hardness = gps_src->hardness;
copy_v2_v2(gps_dst->aspect_ratio, gps_src->aspect_ratio);
gps_dst->fill_opacity_fac = gps_dst->fill_opacity_fac;
gps_dst->fill_opacity_fac = gps_src->fill_opacity_fac;
copy_v3_v3(gps_dst->boundbox_min, gps_src->boundbox_min);
copy_v3_v3(gps_dst->boundbox_max, gps_src->boundbox_max);
gps_dst->uv_rotation = gps_src->uv_rotation;

View File

@ -578,7 +578,7 @@ void Drawing::set_texture_matrices(Span<float4x2> matrices, const IndexMask &sel
const float4x2 texspace = matrices[pos];
/* We do the computation using doubles to avoid numerical precision errors. */
double4x3 strokemat4x3 = double4x3(expand_4x2_mat(strokemat));
const double4x3 strokemat4x3 = double4x3(expand_4x2_mat(strokemat));
/*
* We want to solve for `texture_matrix` in the equation: `texspace = texture_matrix *

View File

@ -784,10 +784,10 @@ void layer_adjustments_to_modifiers(Main &bmain, bGPdata &src_object_data, Objec
*
* NOTE: NLA animation in GPData that would control adjustment properties is not converted. This
* would require (partially) re-creating a copy of the potential bGPData NLA into the Object NLA,
* which is too complex for the few potential usecases.
* which is too complex for the few potential use cases.
*
* This is achieved in several steps, roughly:
* * For each GP layer, chack if there is animation on the adjutment data.
* * For each GP layer, check if there is animation on the adjustment data.
* * Rename relevant FCurves RNA paths from GP animation data, and store their reference in
* temporary vectors.
* * Once all layers have been processed, move all affected FCurves from GPData animation to
@ -838,7 +838,7 @@ void layer_adjustments_to_modifiers(Main &bmain, bGPdata &src_object_data, Objec
BLI_str_escape(layer_name_esc, gpl->info, sizeof(layer_name_esc));
const std::string legacy_root_path = fmt::format("layers[\"{}\"]", layer_name_esc);
/* If tint or thickness are animated, relevamt modifiers also need to be created. */
/* If tint or thickness are animated, relevant modifiers also need to be created. */
if (gpd_animdata) {
auto adjustment_animation_detection = [&](bAction *owner_action, FCurve &fcurve) -> bool {
/* Early out if we already know that both data are animated. */
@ -1840,6 +1840,55 @@ static void legacy_object_modifier_subdiv(Object &object, GpencilModifierData &l
false);
}
static void legacy_object_modifier_texture(Object &object, GpencilModifierData &legacy_md)
{
ModifierData &md = legacy_object_modifier_common(
object, eModifierType_GreasePencilTexture, legacy_md);
auto &md_texture = reinterpret_cast<GreasePencilTextureModifierData &>(md);
auto &legacy_md_texture = reinterpret_cast<TextureGpencilModifierData &>(legacy_md);
switch (eTextureGpencil_Mode(legacy_md_texture.mode)) {
case STROKE:
md_texture.mode = MOD_GREASE_PENCIL_TEXTURE_STROKE;
break;
case FILL:
md_texture.mode = MOD_GREASE_PENCIL_TEXTURE_FILL;
break;
case STROKE_AND_FILL:
md_texture.mode = MOD_GREASE_PENCIL_TEXTURE_STROKE_AND_FILL;
break;
}
switch (eTextureGpencil_Fit(legacy_md_texture.fit_method)) {
case GP_TEX_FIT_STROKE:
md_texture.fit_method = MOD_GREASE_PENCIL_TEXTURE_FIT_STROKE;
break;
case GP_TEX_CONSTANT_LENGTH:
md_texture.fit_method = MOD_GREASE_PENCIL_TEXTURE_CONSTANT_LENGTH;
break;
}
md_texture.uv_offset = legacy_md_texture.uv_offset;
md_texture.uv_scale = legacy_md_texture.uv_scale;
md_texture.fill_rotation = legacy_md_texture.fill_rotation;
copy_v2_v2(md_texture.fill_offset, legacy_md_texture.fill_offset);
md_texture.fill_scale = legacy_md_texture.fill_scale;
md_texture.layer_pass = legacy_md_texture.layer_pass;
md_texture.alignment_rotation = legacy_md_texture.alignment_rotation;
legacy_object_modifier_influence(md_texture.influence,
legacy_md_texture.layername,
legacy_md_texture.layer_pass,
legacy_md_texture.flag & GP_TEX_INVERT_LAYER,
legacy_md_texture.flag & GP_TEX_INVERT_LAYERPASS,
&legacy_md_texture.material,
legacy_md_texture.pass_index,
legacy_md_texture.flag & GP_TEX_INVERT_MATERIAL,
legacy_md_texture.flag & GP_TEX_INVERT_PASS,
legacy_md_texture.vgname,
legacy_md_texture.flag & GP_TEX_INVERT_VGROUP,
nullptr,
false);
}
static void legacy_object_modifier_thickness(Object &object, GpencilModifierData &legacy_md)
{
ModifierData &md = legacy_object_modifier_common(
@ -2286,6 +2335,9 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
case eGpencilModifierType_Subdiv:
legacy_object_modifier_subdiv(object, *gpd_md);
break;
case eGpencilModifierType_Texture:
legacy_object_modifier_texture(object, *gpd_md);
break;
case eGpencilModifierType_Thick:
legacy_object_modifier_thickness(object, *gpd_md);
break;
@ -2310,7 +2362,6 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
case eGpencilModifierType_Simplify:
legacy_object_modifier_simplify(object, *gpd_md);
break;
case eGpencilModifierType_Texture:
break;
}

View File

@ -457,9 +457,7 @@ static ImageGPUTextures image_get_gpu_texture(Image *ima,
if (GPU_mipmap_enabled()) {
GPU_texture_update_mipmap_chain(*tex);
if (ima) {
ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
}
ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
GPU_texture_mipmap_mode(*tex, true, true);
}
else {

View File

@ -431,9 +431,7 @@ static void layer_aov_copy_data(ViewLayer *view_layer_dst,
ListBase *aovs_dst,
const ListBase *aovs_src)
{
if (aovs_src != nullptr) {
BLI_duplicatelist(aovs_dst, aovs_src);
}
BLI_duplicatelist(aovs_dst, aovs_src);
ViewLayerAOV *aov_dst = static_cast<ViewLayerAOV *>(aovs_dst->first);
const ViewLayerAOV *aov_src = static_cast<const ViewLayerAOV *>(aovs_src->first);

View File

@ -287,5 +287,5 @@ int64_t BKE_lightprobe_grid_cache_frame_sample_count(const LightProbeGridCacheFr
return cache->block_len * cube_i(cache->block_size);
}
/* LIGHTPROBE_CACHE_UNIFORM_GRID */
return cache->size[0] * cache->size[1] * cache->size[2];
return int64_t(cache->size[0]) * cache->size[1] * cache->size[2];
}

View File

@ -57,7 +57,7 @@ void mesh_flip_faces(Mesh &mesh, const IndexMask &selection)
}
});
flip_custom_data_type<float4x4>(faces, mesh.corner_data, selection, CD_TANGENT);
flip_custom_data_type<float4>(faces, mesh.corner_data, selection, CD_TANGENT);
flip_custom_data_type<float4>(faces, mesh.corner_data, selection, CD_MLOOPTANGENT);
flip_custom_data_type<short2>(faces, mesh.corner_data, selection, CD_CUSTOMLOOPNORMAL);
flip_custom_data_type<GridPaintMask>(faces, mesh.corner_data, selection, CD_GRID_PAINT_MASK);

View File

@ -548,22 +548,14 @@ void BKE_mesh_calc_loop_tangent_ex(const float (*vert_positions)[3],
*tangent_mask_curr_p = tangent_mask_curr;
/* Update active layer index */
int act_uv_index = (act_uv_n != -1) ?
CustomData_get_layer_index_n(loopdata, CD_PROP_FLOAT2, act_uv_n) :
-1;
if (act_uv_index != -1) {
int tan_index = CustomData_get_named_layer_index(
loopdata, CD_TANGENT, loopdata->layers[act_uv_index].name);
if (const char *active_uv_name = CustomData_get_active_layer_name(loopdata, CD_PROP_FLOAT2)) {
int tan_index = CustomData_get_named_layer_index(loopdata_out, CD_TANGENT, active_uv_name);
CustomData_set_layer_active_index(loopdata_out, CD_TANGENT, tan_index);
} /* else tangent has been built from orco */
/* Update render layer index */
int ren_uv_index = (ren_uv_n != -1) ?
CustomData_get_layer_index_n(loopdata, CD_PROP_FLOAT2, ren_uv_n) :
-1;
if (ren_uv_index != -1) {
int tan_index = CustomData_get_named_layer_index(
loopdata, CD_TANGENT, loopdata->layers[ren_uv_index].name);
if (const char *render_uv_name = CustomData_get_render_layer_name(loopdata, CD_PROP_FLOAT2)) {
int tan_index = CustomData_get_named_layer_index(loopdata_out, CD_TANGENT, render_uv_name);
CustomData_set_layer_render_index(loopdata_out, CD_TANGENT, tan_index);
} /* else tangent has been built from orco */
}

View File

@ -4157,7 +4157,7 @@ static blender::Set<int> get_known_node_types_set()
static bool can_read_node_type(const int type)
{
/* Can always read custom node types. */
if (type == NODE_CUSTOM) {
if (ELEM(type, NODE_CUSTOM, NODE_CUSTOM_GROUP)) {
return true;
}

View File

@ -834,8 +834,8 @@ static int distribute_compare_orig_index(const void *p1, const void *p2, void *u
return -1;
}
if (index1 == index2) {
/* this pointer comparison appears to make qsort stable for glibc,
* and apparently on solaris too, makes the renders reproducible */
/* This pointer comparison appears to make #qsort stable for GLIBC,
* and apparently on SOLARIS too, makes the renders reproducible. */
if (p1 < p2) {
return -1;
}

View File

@ -46,6 +46,7 @@ extern "C" {
# include <libavformat/avformat.h>
# include <libavutil/buffer.h>
# include <libavutil/channel_layout.h>
# include <libavutil/cpu.h>
# include <libavutil/imgutils.h>
# include <libavutil/opt.h>
# include <libavutil/rational.h>
@ -256,14 +257,15 @@ static AVFrame *alloc_picture(AVPixelFormat pix_fmt, int width, int height)
}
/* allocate the actual picture buffer */
int size = av_image_get_buffer_size(pix_fmt, width, height, 1);
const size_t align = av_cpu_max_align();
int size = av_image_get_buffer_size(pix_fmt, width, height, align);
AVBufferRef *buf = av_buffer_alloc(size);
if (buf == nullptr) {
av_frame_free(&f);
return nullptr;
}
av_image_fill_arrays(f->data, f->linesize, buf->data, pix_fmt, width, height, 1);
av_image_fill_arrays(f->data, f->linesize, buf->data, pix_fmt, width, height, align);
f->buf[0] = buf;
f->format = pix_fmt;
f->width = width;

View File

@ -125,11 +125,11 @@ template<typename T, int Size>
[[nodiscard]] inline bool less_or_equal_than(const VecBase<T, Size> &a, const VecBase<T, Size> &b)
{
for (int i = 0; i < Size; i++) {
if (a[i] > b[i]) {
return false;
if (a[i] <= b[i]) {
return true;
}
}
return true;
return false;
}
} // namespace detail

View File

@ -59,7 +59,7 @@ inline void parallel_for_each(Range &&range, const Function &function)
#ifdef WITH_TBB
tbb::parallel_for_each(range, function);
#else
for (auto &value : range) {
for (auto &&value : range) {
function(value);
}
#endif

View File

@ -254,8 +254,8 @@ set(SRC
BLI_implicit_sharing.hh
BLI_implicit_sharing_ptr.hh
BLI_index_mask.hh
BLI_index_mask_fwd.hh
BLI_index_mask_expression.hh
BLI_index_mask_fwd.hh
BLI_index_range.hh
BLI_inplace_priority_queue.hh
BLI_iterator.h
@ -521,8 +521,8 @@ if(WITH_GTESTS)
tests/BLI_heap_simple_test.cc
tests/BLI_heap_test.cc
tests/BLI_implicit_sharing_test.cc
tests/BLI_index_mask_test.cc
tests/BLI_index_mask_expression_test.cc
tests/BLI_index_mask_test.cc
tests/BLI_index_range_test.cc
tests/BLI_inplace_priority_queue_test.cc
tests/BLI_kdopbvh_test.cc

View File

@ -26,6 +26,15 @@
*/
// #define USE_BRUTE_FORCE_ASSERT
/**
* Assert that the angles the iterator is looping over are in order.
* This works as a general rule however it can fail for large near co-linear edges.
* Even though the hull is convex, the angles calculated from the edges may not consistently
* wind in in the same direction. Even when it does occur the angle discrepancy is so small
* that it can be safely ignored.
*/
// #define USE_ANGLE_ITER_ORDER_ASSERT
using namespace blender;
/* -------------------------------------------------------------------- */
@ -233,17 +242,18 @@ int BLI_convexhull_2d(const float (*points)[2], const int points_num, int r_poin
* \{ */
#if defined(USE_BRUTE_FORCE_ASSERT) && !defined(NDEBUG)
static float convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[2],
int points_hull_num)
static float2 convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[2],
int points_hull_num)
{
float area_best = FLT_MAX;
float2 sincos_best = {0.0f, 1.0f}; /* Track the best angle as a unit vector, delaying `atan2`. */
for (int i = 0, i_prev = points_hull_num - 1; i < points_hull_num; i_prev = i++) {
for (int i = 0; i < points_hull_num; i++) {
const int i_next = (i + 1) % points_hull_num;
/* 2D rotation matrix. */
float dvec_length = 0.0f;
const float2 sincos = math::normalize_and_get_length(
float2(points_hull[i]) - float2(points_hull[i_prev]), dvec_length);
float2(points_hull[i_next]) - float2(points_hull[i]), dvec_length);
if (UNLIKELY(dvec_length == 0.0f)) {
continue;
}
@ -274,12 +284,273 @@ static float convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[
}
}
return (area_best != FLT_MAX) ? float(atan2(sincos_best[0], sincos_best[1])) : 0.0f;
return sincos_best;
}
#endif
/** \} */
/* -------------------------------------------------------------------- */
/** \name Hull Angle Iteration
*
* Step over all angles defined by a convex hull in order from 0-90 degrees,
* when angles are converted into their canonical form (see #sincos_canonical).
* \{ */
/**
* Return a canonical version of `sincos` for the purpose of bounding box fitting,
* this maps any `sincos` to 0-1 range for both `sin` & `cos`.
*/
static float2 sincos_canonical(const float2 &sincos)
{
/* Normalize doesn't ensure a `sin` / `cos` of 1.0/-1.0 ensures the other value is zero.
* Without the check for both 0.0 and 1.0, iteration may not be ordered. */
float2 result;
if (sincos[0] < 0.0f) {
if (sincos[1] < 0.0f) {
result[0] = -sincos[0];
result[1] = -sincos[1];
}
else if ((sincos[0] == -1.0f) && (sincos[1] == 0.0f)) {
result[0] = -sincos[0];
result[1] = sincos[1];
}
else {
result[0] = sincos[1];
result[1] = -sincos[0];
}
}
else {
if (sincos[1] < 0.0f) {
result[0] = -sincos[1];
result[1] = sincos[0];
}
else if ((sincos[0] == 0.0f) && (sincos[1] == 1.0f)) {
result[0] = sincos[1];
result[1] = sincos[0];
}
else {
result = sincos;
}
}
/* The range is [1.0, 0.0], it will approach but never return [0.0, 1.0],
* as the canonical version of this value gets flipped to [1.0, 0.0]. */
BLI_assert(result[0] > 0.0f);
BLI_assert(result[1] >= 0.0f);
return result;
}
/**
* An angle calculated from an edge in a convex hull.
*/
struct AngleCanonical {
/** The edges normalized vector. */
float2 sincos;
/** The result of `sincos_canonical(sincos)` */
float2 sincos_canonical;
/** The index value for the edge `sincos` was calculated from, used as a tie breaker. */
int index;
};
static int hull_angle_canonical_cmp(const AngleCanonical &a, const AngleCanonical &b)
{
if (a.sincos_canonical[0] < b.sincos_canonical[0]) {
return -1;
}
if (a.sincos_canonical[0] > b.sincos_canonical[0]) {
return 1;
}
/* Flipped intentionally. */
if (a.sincos_canonical[1] > b.sincos_canonical[1]) {
return -1;
}
if (a.sincos_canonical[1] < b.sincos_canonical[1]) {
return 1;
}
/* Flipped intentionally. */
if (a.index > b.index) {
return -1;
}
if (a.index < b.index) {
return 1;
}
return 0;
}
/**
* This represents an angle at index `index`.
*/
struct HullAngleStep {
/** Single linked list. */
HullAngleStep *next;
/** The current angle value. */
AngleCanonical angle;
/** The next index value to step into. */
int index;
/** Do not seek past this index. */
int index_max;
};
/**
* Iterate over all angles of a convex hull (defined by `points_hull`) in-order.
*/
struct HullAngleIter {
/** Linked list of up to 4 items (kept in order), * to support walking over angles in order. */
HullAngleStep *axis_ordered = nullptr;
/** [X/Y][min/max]. */
HullAngleStep axis[2][2];
/** The convex hull being iterated over. */
const float (*points_hull)[2];
int points_hull_num;
};
static void hull_angle_insert_ordered(HullAngleIter &hiter, HullAngleStep *insert)
{
HullAngleStep **prev_p = &hiter.axis_ordered;
HullAngleStep *iter = hiter.axis_ordered;
while (iter && hull_angle_canonical_cmp(iter->angle, insert->angle) > 0) {
prev_p = &iter->next;
iter = iter->next;
}
*prev_p = insert;
insert->next = iter;
}
static bool convexhull_2d_angle_iter_step_on_axis(const HullAngleIter &hiter, HullAngleStep &hstep)
{
BLI_assert(hstep.index != -1);
while (hstep.index != hstep.index_max) {
const int i_curr = hstep.index;
const int i_next = (hstep.index + 1) % hiter.points_hull_num;
const float2 dir = float2(hiter.points_hull[i_next]) - float2(hiter.points_hull[i_curr]);
float dir_length = 0.0f;
const float2 sincos_test = math::normalize_and_get_length(dir, dir_length);
hstep.index = i_next;
if (LIKELY(dir_length != 0.0f)) {
hstep.angle.sincos = sincos_test;
hstep.angle.sincos_canonical = sincos_canonical(sincos_test);
hstep.angle.index = i_curr;
return true;
}
}
/* Reached the end, signal this axis shouldn't be stepped over. */
hstep.index = -1;
return false;
}
static HullAngleIter convexhull_2d_angle_iter_init(const float (*points_hull)[2],
const int points_hull_num)
{
const int points_hull_num_minus_1 = points_hull_num - 1;
HullAngleIter hiter = {};
/* Aligned with `hiter.axis`. */
float range[2][2];
/* Initialize min-max range from the first point. */
for (int axis = 0; axis < 2; axis++) {
range[axis][0] = points_hull[0][axis];
range[axis][1] = points_hull[0][axis];
}
/* Expand from all other points.
*
* NOTE: Don't attempt to pick either side when there are multiple equal points.
* Walking backwards while checking `sincos_canonical` handles that. */
for (int i = 1; i < points_hull_num; i++) {
const float *p = points_hull[i];
for (int axis = 0; axis < 2; axis++) {
if (range[axis][0] < p[axis]) {
range[axis][0] = p[axis];
hiter.axis[axis][0].index = i;
}
if (range[axis][1] > p[axis]) {
range[axis][1] = p[axis];
hiter.axis[axis][1].index = i;
}
}
}
/* Step backwards, compute the actual `sincos_canonical` because it's possible
* an edge which is not *exactly* axis aligned normalizes to a value which is.
* Instead of attempting to guess when this might happen,
* simply calculate the value and walk backwards for a long as the canonical angle
* has a `sin` of 1.0 (which must always come first). */
for (int axis = 0; axis < 2; axis++) {
for (int i = 0; i < 2; i++) {
int count = 0;
const int i_orig = hiter.axis[axis][i].index;
int i_curr = i_orig, i_prev;
/* Prevent an eternal loop (incredibly unlikely).
* In virtually all cases this will step back once
* (in the case of an axis-aligned edge) or not at all. */
while ((i_prev = (i_curr + points_hull_num_minus_1) % points_hull_num) != i_orig) {
float dir_length = 0.0f;
const float2 sincos_test = math::normalize_and_get_length(
float2(points_hull[i_curr]) - float2(points_hull[i_prev]), dir_length);
if (LIKELY(dir_length != 0.0f)) {
/* Account for 90 degree corners that may also have an axis-aligned canonical angle. */
if (math::abs(sincos_test[axis]) > 0.5f) {
break;
}
const float2 sincos_test_canonical = sincos_canonical(sincos_test);
if (LIKELY(sincos_test_canonical[0] != 1.0f)) {
break;
}
}
i_curr = i_prev;
hiter.axis[axis][i].index = i_curr;
count++;
}
}
}
/* Setup counter-clockwise limits. */
hiter.axis[0][0].index_max = hiter.axis[1][0].index; /* West to south. */
hiter.axis[1][0].index_max = hiter.axis[0][1].index; /* South to east. */
hiter.axis[0][1].index_max = hiter.axis[1][1].index; /* East to north. */
hiter.axis[1][1].index_max = hiter.axis[0][0].index; /* North to west. */
hiter.points_hull = points_hull;
hiter.points_hull_num = points_hull_num;
for (int axis = 0; axis < 2; axis++) {
for (int i = 0; i < 2; i++) {
hiter.axis[axis][i].angle.index = hiter.axis[axis][i].index;
if (convexhull_2d_angle_iter_step_on_axis(hiter, hiter.axis[axis][i])) {
hull_angle_insert_ordered(hiter, &hiter.axis[axis][i]);
}
}
}
return hiter;
}
static void convexhull_2d_angle_iter_step(HullAngleIter &hiter)
{
HullAngleStep *hstep = hiter.axis_ordered;
#ifdef USE_ANGLE_ITER_ORDER_ASSERT
const AngleCanonical angle_prev = hstep->angle;
#endif
hiter.axis_ordered = hiter.axis_ordered->next;
if (convexhull_2d_angle_iter_step_on_axis(hiter, *hstep)) {
hull_angle_insert_ordered(hiter, hstep);
}
#ifdef USE_ANGLE_ITER_ORDER_ASSERT
if (hiter.axis_ordered) {
hstep = hiter.axis_ordered;
BLI_assert(hull_angle_canonical_cmp(angle_prev, hiter.axis_ordered->angle) > 0);
}
#endif
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Comupte AABB Fitting Angle (Optimized)
* \{ */
@ -295,18 +566,12 @@ static float convexhull_2d_compute_extent_on_axis(const float (*points_hull)[2],
const float2 &sincos,
int *index_p)
{
/* NOTE(@ideasman42): This could be optimized to use a search strategy
* that computes the upper bounds and narrows down the result instead of
* simply checking every point until the new maximum is reached.
* From looking into I couldn't find cases where doing this has significant benefits,
* especially when compared with the complexity of using more involved logic for
* the common case, where only a few steps are needed.
* Typically the number of points to scan is small (around [0..8]).
* And while a high-detail hull with single outliner points will cause stepping over
* many more points, in practice there are rarely more than a few of these in a convex-hull.
* Nevertheless, a high-poly hull that has subtle curves containing many points as well as
* some sharp-corners wont perform as well with this method. */
/* NOTE(@ideasman42): Use a forward search instead of attempting a search strategy
* computing upper & lower bounds (similar to a binary search). The rotating calipers
* are ensured to test ordered rotations between 0-90 degrees, meaning any cases where
* this function needs to step over many points will be limited to a small number of cases.
* Since scanning forward isn't expensive it shouldn't pose a problem. */
BLI_assert(*index_p >= 0);
const int index_init = *index_p;
int index_best = index_init;
float value_init = (Axis == 0) ? sincos_rotate_cw_x(sincos, points_hull[index_best]) :
@ -331,74 +596,46 @@ static float convexhull_2d_compute_extent_on_axis(const float (*points_hull)[2],
static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int points_hull_num)
{
float area_best = FLT_MAX;
float2 sincos_best; /* Track the best angle as a unit vector, delaying `atan2`. */
bool is_first = true;
float2 sincos_best = {0.0f, 1.0f}; /* Track the best angle as a unit vector, delaying `atan2`. */
int index_best = INT_MAX;
/* Initialize to zero because the first pass uses the first index to set the bounds. */
blender::Bounds<int> bounds_index[2] = {{0, 0}, {0, 0}};
for (int i = 0, i_prev = points_hull_num - 1; i < points_hull_num; i_prev = i++) {
/* 2D rotation matrix. */
float dvec_length = 0.0f;
const float2 sincos = math::normalize_and_get_length(
float2(points_hull[i]) - float2(points_hull[i_prev]), dvec_length);
if (UNLIKELY(dvec_length == 0.0f)) {
continue;
}
if (UNLIKELY(is_first)) {
is_first = false;
blender::Bounds<float> bounds[2];
bounds[0].min = bounds[0].max = sincos_rotate_cw_x(sincos, points_hull[0]);
bounds[1].min = bounds[1].max = sincos_rotate_cw_y(sincos, points_hull[0]);
bounds_index[0].min = bounds_index[0].max = 0;
bounds_index[1].min = bounds_index[1].max = 0;
for (int j = 1; j < points_hull_num; j++) {
const float2 tvec = {
sincos_rotate_cw_x(sincos, points_hull[j]),
sincos_rotate_cw_y(sincos, points_hull[j]),
};
for (int axis = 0; axis < 2; axis++) {
if (tvec[axis] < bounds[axis].min) {
bounds[axis].min = tvec[axis];
bounds_index[axis].min = j;
}
if (tvec[axis] > bounds[axis].max) {
bounds[axis].max = tvec[axis];
bounds_index[axis].max = j;
}
}
}
area_best = (bounds[0].max - bounds[0].min) * (bounds[1].max - bounds[1].min);
sincos_best = sincos;
continue;
}
HullAngleIter hull_iter = convexhull_2d_angle_iter_init(points_hull, points_hull_num);
/* Use the axis aligned bounds as starting points. */
bounds_index[0].min = hull_iter.axis[0][1].angle.index;
bounds_index[0].max = hull_iter.axis[0][0].angle.index;
bounds_index[1].min = hull_iter.axis[1][0].angle.index;
bounds_index[1].max = hull_iter.axis[1][1].angle.index;
while (const HullAngleStep *hstep = hull_iter.axis_ordered) {
/* Step the calipers to the new rotation `sincos`, returning the bounds at the same time. */
blender::Bounds<float> bounds_test[2] = {
{convexhull_2d_compute_extent_on_axis<0, -1>(
points_hull, points_hull_num, sincos, &bounds_index[0].min),
points_hull, points_hull_num, hstep->angle.sincos_canonical, &bounds_index[0].min),
convexhull_2d_compute_extent_on_axis<0, 1>(
points_hull, points_hull_num, sincos, &bounds_index[0].max)},
points_hull, points_hull_num, hstep->angle.sincos_canonical, &bounds_index[0].max)},
{convexhull_2d_compute_extent_on_axis<1, -1>(
points_hull, points_hull_num, sincos, &bounds_index[1].min),
points_hull, points_hull_num, hstep->angle.sincos_canonical, &bounds_index[1].min),
convexhull_2d_compute_extent_on_axis<1, 1>(
points_hull, points_hull_num, sincos, &bounds_index[1].max)},
points_hull, points_hull_num, hstep->angle.sincos_canonical, &bounds_index[1].max)},
};
const float area_test = (bounds_test[0].max - bounds_test[0].min) *
(bounds_test[1].max - bounds_test[1].min);
if (area_test < area_best) {
if (area_test < area_best ||
/* Use the index as a tie breaker, this simply matches the behavior of checking
* all edges in-order and only overwriting past results when they're an improvement. */
((area_test == area_best) && (hstep->angle.index < index_best)))
{
area_best = area_test;
sincos_best = sincos;
sincos_best = hstep->angle.sincos;
index_best = hstep->angle.index;
}
convexhull_2d_angle_iter_step(hull_iter);
}
const float angle = (area_best != FLT_MAX) ? float(atan2(sincos_best[0], sincos_best[1])) : 0.0f;
@ -406,8 +643,11 @@ static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int poin
#if defined(USE_BRUTE_FORCE_ASSERT) && !defined(NDEBUG)
{
/* Ensure the optimized result matches the brute-force version. */
const float angle_test = convexhull_aabb_fit_hull_2d_brute_force(points_hull, points_hull_num);
BLI_assert(angle == angle_test);
const float2 sincos_test = convexhull_aabb_fit_hull_2d_brute_force(points_hull,
points_hull_num);
if (sincos_best != sincos_test) {
BLI_assert(sincos_best == sincos_test);
}
}
#endif

View File

@ -475,31 +475,31 @@ int BLI_rename(const char *from, const char *to)
#ifdef WIN32
return urename(from, to, false);
#elif defined(__APPLE__)
return renamex_np(from, to, RENAME_EXCL);
#else
# if defined(__APPLE__)
int ret = renamex_np(from, to, RENAME_EXCL);
if (!(ret < 0 && errno == ENOTSUP)) {
return ret;
}
# endif
# if defined(__GLIBC_PREREQ)
# if __GLIBC_PREREQ(2, 28)
/* Most common Linux case, use `RENAME_NOREPLACE` when available. */
{
const int ret = renameat2(AT_FDCWD, from, AT_FDCWD, to, RENAME_NOREPLACE);
if (!(ret < 0 && errno == EINVAL)) {
return ret;
}
/* Most likely a file-system that doesn't support RENAME_NOREPLACE.
* (For example NFS, Samba, exFAT, NTFS, etc)
* Fall through to use the generic UNIX non atomic operation, see #116049. */
int ret = renameat2(AT_FDCWD, from, AT_FDCWD, to, RENAME_NOREPLACE);
if (!(ret < 0 && errno == EINVAL)) {
return ret;
}
# endif /* __GLIBC_PREREQ(2, 28) */
# endif /* __GLIBC_PREREQ */
/* All BSD's currently & fallback for Linux. */
/* A naive non-atomic implementation, which is used for OS where atomic rename is not supported
* at all, or not implemented for specific file systems (for example NFS, Samba, exFAT, NTFS,
* etc). For those see #116049, #119966. */
if (BLI_exists(to)) {
return 1;
}
return rename(from, to);
#endif /* !defined(WIN32) && !defined(__APPLE__) */
#endif /* !defined(WIN32) */
}
int BLI_rename_overwrite(const char *from, const char *to)

View File

@ -490,21 +490,40 @@ BLI_INLINE float perlin_noise(float4 position)
float perlin_signed(float position)
{
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
position = math::mod(position, 100000.0f);
return perlin_noise(position) * 0.2500f;
}
float perlin_signed(float2 position)
{
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
position = math::mod(position, 100000.0f);
return perlin_noise(position) * 0.6616f;
}
float perlin_signed(float3 position)
{
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
position = math::mod(position, 100000.0f);
return perlin_noise(position) * 0.9820f;
}
float perlin_signed(float4 position)
{
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
position = math::mod(position, 100000.0f);
return perlin_noise(position) * 0.8344f;
}

View File

@ -21,6 +21,7 @@
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_rotation.h"
#include "BLI_math_rotation.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
@ -146,7 +147,7 @@ TEST(convexhull_2d, Lines_AxisAligned)
for (int sign_x = -1; sign_x <= 2; sign_x += 2) {
blender::Array<float2> points = {{0.0f, 0.0f}, {1.0f * sign_x, 0.0}};
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points),
float(math::AngleRadian::from_degree(-90.0f)),
float(math::AngleRadian::from_degree(90.0f)),
ROTATION_EPS);
}
}
@ -154,7 +155,7 @@ TEST(convexhull_2d, Lines_AxisAligned)
for (int sign_x = -1; sign_x <= 2; sign_x += 2) {
blender::Array<float2> points = {{0.0f, 0.0f}, {1.0f * sign_x, 0.0}, {2.0f * sign_x, 0.0}};
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points),
float(math::AngleRadian::from_degree(-90.0f)),
float(math::AngleRadian::from_degree(90.0f)),
ROTATION_EPS);
}
}
@ -163,7 +164,7 @@ TEST(convexhull_2d, Lines_AxisAligned)
for (int sign_y = -1; sign_y <= 2; sign_y += 2) {
blender::Array<float2> points = {{0.0f, 0.0f}, {0.0f, 1.0f * sign_y}};
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points),
float(math::AngleRadian::from_degree(180.0f)),
float(math::AngleRadian::from_degree(0.0f)),
ROTATION_EPS);
}
}
@ -171,7 +172,7 @@ TEST(convexhull_2d, Lines_AxisAligned)
for (int sign_y = -1; sign_y <= 2; sign_y += 2) {
blender::Array<float2> points = {{0.0f, 0.0f}, {0.0f, 1.0f * sign_y}, {0.0f, 2.0f * sign_y}};
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points),
float(math::AngleRadian::from_degree(180.0f)),
float(math::AngleRadian::from_degree(0.0f)),
ROTATION_EPS);
}
}
@ -186,7 +187,7 @@ TEST(convexhull_2d, Lines_AxisAligned)
p[0] = 0.0;
}
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points), M_PI, ROTATION_EPS);
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points), 0.0f, ROTATION_EPS);
}
}
@ -201,7 +202,7 @@ TEST(convexhull_2d, Lines_AxisAligned)
}
blender::Array<float2> points_hull = convexhull_2d_as_array(points);
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points_hull), M_PI, ROTATION_EPS);
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points_hull), 0.0f, ROTATION_EPS);
}
}
}
@ -209,7 +210,7 @@ TEST(convexhull_2d, Lines_AxisAligned)
TEST(convexhull_2d, Lines_Diagonal)
{
{ /* Diagonal line (2 points). */
const float expected[4] = {-135, 135, 135, -135};
const float expected[4] = {45, -45, -45, 45};
int index = 0;
for (int sign_x = -1; sign_x <= 2; sign_x += 2) {
for (int sign_y = -1; sign_y <= 2; sign_y += 2) {
@ -223,7 +224,7 @@ TEST(convexhull_2d, Lines_Diagonal)
}
{ /* Diagonal line (3 points). */
const float expected[4] = {-135, 135, 135, -135};
const float expected[4] = {45, -45, -45, 45};
int index = 0;
for (int sign_x = -1; sign_x <= 2; sign_x += 2) {
for (int sign_y = -1; sign_y <= 2; sign_y += 2) {
@ -251,7 +252,7 @@ TEST(convexhull_2d, Simple)
{1.0f, 0.0f},
};
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points),
float(math::AngleRadian::from_degree(135.0f)),
float(math::AngleRadian::from_degree(45.0f)),
ROTATION_EPS);
}
@ -263,7 +264,51 @@ TEST(convexhull_2d, Simple)
{1.0f, -1.0f},
};
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points),
float(math::AngleRadian::from_degree(180.0f)),
float(math::AngleRadian::from_degree(90.0f)),
ROTATION_EPS);
}
}
TEST(convexhull_2d, Octagon)
{
auto shape_octagon_fn = [](RandomNumberGenerator &rng,
const int points_num) -> blender::Array<float2> {
/* Avoid zero area boxes. */
blender::Array<float2> points(points_num);
for (int i = 0; i < points_num; i++) {
sin_cos_from_fraction(i, points_num, &points[i][0], &points[i][1]);
}
rng.shuffle<float2>(points);
return points;
};
RandomNumberGenerator rng = RandomNumberGenerator(DEFAULT_TEST_RANDOM_SEED);
for (int iter = 0; iter < DEFAULT_TEST_ITER; iter++) {
blender::Array<float2> points = shape_octagon_fn(rng, 8);
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points),
float(math::AngleRadian::from_degree(67.5f)),
ROTATION_EPS);
}
}
TEST(convexhull_2d, OctagonAxisAligned)
{
auto shape_octagon_fn = [](RandomNumberGenerator &rng,
const int points_num) -> blender::Array<float2> {
/* Avoid zero area boxes. */
blender::Array<float2> points(points_num);
for (int i = 0; i < points_num; i++) {
sin_cos_from_fraction((i * 2) + 1, points_num * 2, &points[i][0], &points[i][1]);
}
rng.shuffle<float2>(points);
return points;
};
RandomNumberGenerator rng = RandomNumberGenerator(DEFAULT_TEST_RANDOM_SEED);
for (int iter = 0; iter < DEFAULT_TEST_ITER; iter++) {
blender::Array<float2> points = shape_octagon_fn(rng, 8);
EXPECT_NEAR(convexhull_2d_aabb_fit_points_2d(points),
float(math::AngleRadian::from_degree(90.0f)),
ROTATION_EPS);
}
}
@ -343,4 +388,58 @@ TEST(convexhull_2d, Complex)
}
}
/* Keep these as they're handy for generating a lot of random data.
* To brute force check results are as expected:
* - Increase #DEFAULT_TEST_ITER to a large number (100k or so).
* - Uncomment #USE_BRUTE_FORCE_ASSERT define in `convexhull_2d.cc` to ensure results
* match a reference implementation.
*/
#if 0
TEST(convexhull_2d, Circle)
{
auto shape_circle_fn = [](RandomNumberGenerator &rng,
const int points_num) -> blender::Array<float2> {
/* Avoid zero area boxes. */
blender::Array<float2> points(points_num);
/* Going this way ends up with normal(s) upward */
for (int i = 0; i < points_num; i++) {
sin_cos_from_fraction(i, points_num, &points[i][0], &points[i][1]);
}
rng.shuffle<float2>(points);
return points;
};
RandomNumberGenerator rng = RandomNumberGenerator(DEFAULT_TEST_RANDOM_SEED);
for (int iter = 0; iter < DEFAULT_TEST_ITER; iter++) {
blender::Array<float2> points = shape_circle_fn(rng, DEFAULT_TEST_POLY_NUM);
const float angle = convexhull_2d_aabb_fit_points_2d(points);
(void)angle;
}
}
TEST(convexhull_2d, Random)
{
auto shape_random_unit_fn = [](RandomNumberGenerator &rng,
const int points_num) -> blender::Array<float2> {
/* Avoid zero area boxes. */
blender::Array<float2> points(points_num);
/* Going this way ends up with normal(s) upward */
for (int i = 0; i < points_num; i++) {
points[i] = rng.get_unit_float2();
}
return points;
};
RandomNumberGenerator rng = RandomNumberGenerator(DEFAULT_TEST_RANDOM_SEED);
for (int iter = 0; iter < DEFAULT_TEST_ITER; iter++) {
blender::Array<float2> points = shape_random_unit_fn(rng, DEFAULT_TEST_POLY_NUM);
const float angle = convexhull_2d_aabb_fit_points_2d(points);
(void)angle;
}
}
#endif
/** \} */

View File

@ -49,8 +49,8 @@ class ConversionOperation : public SimpleOperation {
/* -------------------------------------------------------------------- */
/** \name Convert Float to Vector Operation
*
* Takes a float result and outputs a vector result. All three components of the output are filled
* with the input float.
* Takes a float result and outputs a vector result. The first three components of the output are
* filled with the input float, while the fourth component is set to 1.
* \{ */
class ConvertFloatToVectorOperation : public ConversionOperation {
@ -103,8 +103,8 @@ class ConvertColorToFloatOperation : public ConversionOperation {
/* -------------------------------------------------------------------- */
/** \name Convert Color to Vector Operation
*
* Takes a color result and outputs a vector result. The output is a copy of the three color
* channels to the three vector components.
* Takes a color result and outputs a vector result. The output is an exact copy of the input since
* vectors are 4D.
* \{ */
class ConvertColorToVectorOperation : public ConversionOperation {

View File

@ -98,7 +98,7 @@ ConvertFloatToVectorOperation::ConvertFloatToVectorOperation(Context &context)
void ConvertFloatToVectorOperation::execute_single(const Result &input, Result &output)
{
output.set_vector_value(float4(float3(input.get_float_value()), 0.0f));
output.set_vector_value(float4(float3(input.get_float_value()), 1.0f));
}
GPUShader *ConvertFloatToVectorOperation::get_conversion_shader() const
@ -177,7 +177,7 @@ ConvertColorToVectorOperation::ConvertColorToVectorOperation(Context &context)
void ConvertColorToVectorOperation::execute_single(const Result &input, Result &output)
{
float4 color = input.get_color_value();
output.set_vector_value(float4(float3(color), 0.0f));
output.set_vector_value(color);
}
GPUShader *ConvertColorToVectorOperation::get_conversion_shader() const

View File

@ -19,7 +19,7 @@ GPU_SHADER_CREATE_INFO(compositor_convert_float_to_float)
GPU_SHADER_CREATE_INFO(compositor_convert_float_to_vector)
.additional_info("compositor_convert_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_float(value.x), 0.0)")
.define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_float(value.x), 1.0)")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_convert_float_to_color)
@ -37,7 +37,7 @@ GPU_SHADER_CREATE_INFO(compositor_convert_color_to_float)
GPU_SHADER_CREATE_INFO(compositor_convert_color_to_vector)
.additional_info("compositor_convert_shared")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_vec4(value), 0.0)")
.define("CONVERT_EXPRESSION(value)", "value")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(compositor_convert_color_to_color)

View File

@ -237,17 +237,17 @@ set(SRC
intern/draw_cache_inline.hh
intern/draw_color_management.hh
intern/draw_command.hh
intern/draw_common_c.hh
intern/draw_common.hh
intern/draw_common_c.hh
intern/draw_common_shader_shared.hh
intern/draw_curves_private.hh
intern/draw_debug_c.hh
intern/draw_debug.hh
intern/draw_debug_c.hh
intern/draw_hair_private.hh
intern/draw_handle.hh
intern/draw_instance_data.hh
intern/draw_manager_c.hh
intern/draw_manager.hh
intern/draw_manager_c.hh
intern/draw_manager_profiling.hh
intern/draw_manager_testing.hh
intern/draw_manager_text.hh
@ -260,8 +260,8 @@ set(SRC
intern/draw_state.hh
intern/draw_subdivision.hh
intern/draw_texture_pool.hh
intern/draw_view_c.hh
intern/draw_view.hh
intern/draw_view_c.hh
intern/draw_view_data.hh
intern/mesh_extractors/extract_mesh.hh
engines/basic/basic_engine.h
@ -586,6 +586,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_shadow_tag_usage_vert.glsl
engines/eevee_next/shaders/eevee_shadow_tag_usage_volume_comp.glsl
engines/eevee_next/shaders/eevee_shadow_test.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_amend_comp.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_bounds_comp.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl

View File

@ -246,10 +246,12 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata,
}
if (draw_ctx->rv3d) {
copy_v4_v4(sldata->common_data.camera_uv_scale, draw_ctx->rv3d->viewcamtexcofac);
copy_v2_v2(sldata->common_data.camera_uv_scale, &draw_ctx->rv3d->viewcamtexcofac[0]);
copy_v2_v2(sldata->common_data.camera_uv_bias, &draw_ctx->rv3d->viewcamtexcofac[2]);
}
else {
copy_v4_fl4(sldata->common_data.camera_uv_scale, 1.0f, 1.0f, 0.0f, 0.0f);
copy_v2_fl2(sldata->common_data.camera_uv_scale, 1.0f, 1.0f);
copy_v2_fl2(sldata->common_data.camera_uv_bias, 0.0f, 0.0f);
}
if (!DRW_state_is_image_render() && ((stl->effects->enabled_effects & EFFECT_TAA) == 0)) {

View File

@ -15,8 +15,7 @@ void Cryptomatte::begin_sync()
{
const eViewLayerEEVEEPassType enabled_passes = static_cast<eViewLayerEEVEEPassType>(
inst_.film.enabled_passes_get() &
(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET |
EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET));
(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET));
session_.reset();
object_layer_ = nullptr;

View File

@ -87,6 +87,8 @@
#define SHADOW_TILEDATA_PER_TILEMAP \
(SHADOW_TILEMAP_LOD0_LEN + SHADOW_TILEMAP_LOD1_LEN + SHADOW_TILEMAP_LOD2_LEN + \
SHADOW_TILEMAP_LOD3_LEN + SHADOW_TILEMAP_LOD4_LEN + SHADOW_TILEMAP_LOD5_LEN)
/* Maximum number of relative LOD distance we can store. */
#define SHADOW_TILEMAP_MAX_CLIPMAP_LOD 8
#if 0
/* Useful for debugging the tile-copy version of the shadow rendering without making debugging
* tools unresponsive. */

View File

@ -1226,7 +1226,7 @@ LightProbeGridCacheFrame *IrradianceBake::read_result_packed()
cache_frame->baking.L1_c = (float(*)[4])irradiance_L1_c_tx_.read<float4>(GPU_DATA_FLOAT);
cache_frame->baking.validity = (float *)validity_tx_.read<float>(GPU_DATA_FLOAT);
int64_t sample_count = irradiance_L0_tx_.width() * irradiance_L0_tx_.height() *
int64_t sample_count = int64_t(irradiance_L0_tx_.width()) * irradiance_L0_tx_.height() *
irradiance_L0_tx_.depth();
size_t coefficient_texture_size = sizeof(*cache_frame->irradiance.L0) * sample_count;
size_t validity_texture_size = sizeof(*cache_frame->connectivity.validity) * sample_count;

View File

@ -241,6 +241,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_shadow_page_free";
case SHADOW_PAGE_MASK:
return "eevee_shadow_page_mask";
case SHADOW_TILEMAP_AMEND:
return "eevee_shadow_tilemap_amend";
case SHADOW_TILEMAP_BOUNDS:
return "eevee_shadow_tilemap_bounds";
case SHADOW_TILEMAP_FINALIZE:

View File

@ -120,6 +120,7 @@ enum eShaderType {
SHADOW_PAGE_MASK,
SHADOW_PAGE_TILE_CLEAR,
SHADOW_PAGE_TILE_STORE,
SHADOW_TILEMAP_AMEND,
SHADOW_TILEMAP_BOUNDS,
SHADOW_TILEMAP_FINALIZE,
SHADOW_TILEMAP_INIT,

View File

@ -1231,8 +1231,6 @@ struct ShadowTileData {
uint3 page;
/** Page index inside pages_cached_buf. Only valid if `is_cached` is true. */
uint cache_index;
/** LOD pointed to LOD 0 tile page. (cube-map only). */
uint lod;
/** If the tile is needed for rendering. */
bool is_used;
/** True if an update is needed. This persists even if the tile gets unused. */
@ -1279,8 +1277,7 @@ static inline ShadowTileData shadow_tile_unpack(ShadowTileDataPacked data)
ShadowTileData tile;
tile.page = shadow_page_unpack(data);
/* -- 12 bits -- */
BLI_STATIC_ASSERT(SHADOW_TILEMAP_LOD < 8, "Update page packing")
tile.lod = (data >> 12u) & 7u;
/* Unused bits. */
/* -- 15 bits -- */
BLI_STATIC_ASSERT(SHADOW_MAX_PAGE <= 4096, "Update page packing")
tile.cache_index = (data >> 15u) & 4095u;
@ -1299,7 +1296,6 @@ static inline ShadowTileDataPacked shadow_tile_pack(ShadowTileData tile)
/* NOTE: Page might be set to invalid values for tracking invalid usages.
* So we have to mask the result. */
data = shadow_page_pack(tile.page) & uint(SHADOW_MAX_PAGE - 1);
data |= (tile.lod & 7u) << 12u;
data |= (tile.cache_index & 4095u) << 15u;
data |= (tile.is_used ? uint(SHADOW_IS_USED) : 0);
data |= (tile.is_allocated ? uint(SHADOW_IS_ALLOCATED) : 0);
@ -1309,6 +1305,89 @@ static inline ShadowTileDataPacked shadow_tile_pack(ShadowTileData tile)
return data;
}
/**
* Decoded tile data structure.
* Similar to ShadowTileData, this one is only used for rendering and packed into `tilemap_tx`.
* This allow to reuse some bits for other purpose.
*/
struct ShadowSamplingTile {
/** Page inside the virtual shadow map atlas. */
uint3 page;
/** LOD pointed to LOD 0 tile page. */
uint lod;
/** Offset to the texel position to align with the LOD page start. (directional only). */
uint2 lod_offset;
/** If the tile is needed for rendering. */
bool is_valid;
};
/** \note Stored packed as a uint. */
#define ShadowSamplingTilePacked uint
/* NOTE: Trust the input to be in valid range [0, (1 << SHADOW_TILEMAP_MAX_CLIPMAP_LOD) - 1].
* Maximum LOD level index we can store is SHADOW_TILEMAP_MAX_CLIPMAP_LOD,
* so we need SHADOW_TILEMAP_MAX_CLIPMAP_LOD bits to store the offset in each dimension.
* Result fits into SHADOW_TILEMAP_MAX_CLIPMAP_LOD * 2 bits. */
static inline uint shadow_lod_offset_pack(uint2 ofs)
{
BLI_STATIC_ASSERT(SHADOW_TILEMAP_MAX_CLIPMAP_LOD <= 8, "Update page packing")
return ofs.x | (ofs.y << SHADOW_TILEMAP_MAX_CLIPMAP_LOD);
}
static inline uint2 shadow_lod_offset_unpack(uint data)
{
return (uint2(data) >> uint2(0, SHADOW_TILEMAP_MAX_CLIPMAP_LOD)) &
uint2((1 << SHADOW_TILEMAP_MAX_CLIPMAP_LOD) - 1);
}
static inline ShadowSamplingTile shadow_sampling_tile_unpack(ShadowSamplingTilePacked data)
{
ShadowSamplingTile tile;
tile.page = shadow_page_unpack(data);
/* -- 12 bits -- */
/* Max value is actually SHADOW_TILEMAP_MAX_CLIPMAP_LOD but we mask the bits. */
tile.lod = (data >> 12u) & 15u;
/* -- 16 bits -- */
tile.lod_offset = shadow_lod_offset_unpack(data >> 16u);
/* -- 32 bits -- */
tile.is_valid = data != 0u;
#ifndef GPU_SHADER
/* Make tests pass on CPU but it is not required for proper rendering. */
if (tile.lod == 0) {
tile.lod_offset.x = 0;
}
#endif
return tile;
}
static inline ShadowSamplingTilePacked shadow_sampling_tile_pack(ShadowSamplingTile tile)
{
if (!tile.is_valid) {
return 0u;
}
/* Tag a valid tile of LOD0 valid by setting their offset to 1.
* This doesn't change the sampling and allows to use of all bits for data.
* This makes sure no valid packed tile is 0u. */
if (tile.lod == 0) {
tile.lod_offset.x = 1;
}
uint data = shadow_page_pack(tile.page);
/* Max value is actually SHADOW_TILEMAP_MAX_CLIPMAP_LOD but we mask the bits. */
data |= (tile.lod & 15u) << 12u;
data |= shadow_lod_offset_pack(tile.lod_offset) << 16u;
return data;
}
static inline ShadowSamplingTile shadow_sampling_tile_create(ShadowTileData tile_data, uint lod)
{
ShadowSamplingTile tile;
tile.page = tile_data.page;
tile.lod = lod;
tile.lod_offset = uint2(0, 0); /* Computed during tilemap amend phase. */
/* At this point, it should be the case that all given tiles that have been tagged as used are
* ready for sampling. Otherwise tile_data should be SHADOW_NO_DATA. */
tile.is_valid = tile_data.is_used;
return tile;
}
struct ShadowSceneData {
/* Number of shadow rays to shoot for each light. */
int ray_count;

View File

@ -1199,6 +1199,15 @@ void ShadowModule::end_sync()
sub.barrier(GPU_BARRIER_SHADER_STORAGE | GPU_BARRIER_UNIFORM | GPU_BARRIER_TEXTURE_FETCH |
GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
{
/* Amend tilemap_tx content to support clipmap LODs. */
PassSimple::Sub &sub = pass.sub("Amend");
sub.shader_set(inst_.shaders.static_shader_get(SHADOW_TILEMAP_AMEND));
sub.bind_image("tilemaps_img", tilemap_pool.tilemap_tx);
sub.bind_resources(inst_.lights);
sub.dispatch(int3(1));
sub.barrier(GPU_BARRIER_TEXTURE_FETCH);
}
/* NOTE: We do not need to run the clear pass when using the TBDR update variant, as tiles
* will be fully cleared as part of the shadow raster step. */

View File

@ -33,7 +33,6 @@ void debug_tile_print(ShadowTileData tile, ivec4 tile_coord)
{
#ifdef DRW_DEBUG_PRINT
drw_print("Tile (", tile_coord.x, ",", tile_coord.y, ") in Tilemap ", tile_coord.z, " : ");
drw_print(tile.lod);
drw_print(tile.page);
drw_print(tile.cache_index);
#endif
@ -41,10 +40,6 @@ void debug_tile_print(ShadowTileData tile, ivec4 tile_coord)
vec3 debug_tile_state_color(ShadowTileData tile)
{
if (tile.lod > 0) {
/* Uses data from another LOD. */
return neon_gradient(float(tile.lod) / float(SHADOW_TILEMAP_LOD));
}
if (tile.do_update && tile.is_used) {
/* Updated. */
return vec3(0.5, 1, 0);
@ -63,6 +58,17 @@ vec3 debug_tile_state_color(ShadowTileData tile)
return col;
}
vec3 debug_tile_state_color(eLightType type, ShadowSamplingTile tile)
{
if (!tile.is_valid) {
return vec3(1, 0, 0);
}
/* Uses data from another LOD. */
return neon_gradient(float(tile.lod) / float((type == LIGHT_SUN) ?
SHADOW_TILEMAP_MAX_CLIPMAP_LOD :
SHADOW_TILEMAP_LOD));
}
ShadowSampleParams debug_shadow_sample_get(vec3 P, LightData light)
{
if (is_sun_light(light.type)) {
@ -73,7 +79,7 @@ ShadowSampleParams debug_shadow_sample_get(vec3 P, LightData light)
}
}
ShadowTileData debug_tile_get(vec3 P, LightData light)
ShadowSamplingTile debug_tile_get(vec3 P, LightData light)
{
return shadow_tile_data_get(shadow_tilemaps_tx, debug_shadow_sample_get(P, light));
}
@ -106,6 +112,26 @@ bool debug_tilemaps(vec3 P, LightData light)
int tilemap = px.x / SHADOW_TILEMAP_RES;
int tilemap_index = light.tilemap_index + tilemap;
if ((px.y < SHADOW_TILEMAP_RES) && (tilemap_index <= light_tilemap_max_get(light))) {
#if 1
/* Debug values in the tilemap_tx. */
ivec2 tilemap_texel = shadow_tile_coord_in_atlas(px, tilemap_index);
ShadowSamplingTile tile = shadow_sampling_tile_unpack(
texelFetch(shadow_tilemaps_tx, tilemap_texel, 0).x);
/* Leave 1 px border between tile-maps. */
if (!any(equal(ivec2(gl_FragCoord.xy) % (SHADOW_TILEMAP_RES * debug_tile_size_px), ivec2(0))))
{
gl_FragDepth = 0.0;
out_color_add = vec4(debug_tile_state_color(light.type, tile), 0.0);
out_color_mul = vec4(0.0);
# ifdef DRW_DEBUG_PRINT
if (all(equal(ivec2(gl_FragCoord.xy), ivec2(0)))) {
drw_print(light.object_mat);
}
# endif
return true;
}
#else
/* Debug actual values in the tile-map buffer. */
ShadowTileMapData tilemap = tilemaps_buf[tilemap_index];
int tile_index = shadow_tile_offset(
@ -118,21 +144,22 @@ bool debug_tilemaps(vec3 P, LightData light)
out_color_add = vec4(debug_tile_state_color(tile), 0.0);
out_color_mul = vec4(0.0);
#ifdef DRW_DEBUG_PRINT
# ifdef DRW_DEBUG_PRINT
if (all(equal(ivec2(gl_FragCoord.xy), ivec2(0)))) {
drw_print(light.object_mat);
}
#endif
# endif
return true;
}
#endif
}
return false;
}
void debug_tile_state(vec3 P, LightData light)
{
ShadowTileData tile = debug_tile_get(P, light);
out_color_add = vec4(debug_tile_state_color(tile), 0) * 0.5;
ShadowSamplingTile tile = debug_tile_get(P, light);
out_color_add = vec4(debug_tile_state_color(light.type, tile), 0) * 0.5;
out_color_mul = vec4(0.5);
}
@ -146,7 +173,7 @@ void debug_atlas_values(vec3 P, LightData light)
void debug_random_tile_color(vec3 P, LightData light)
{
ShadowTileData tile = debug_tile_get(P, light);
ShadowSamplingTile tile = debug_tile_get(P, light);
out_color_add = vec4(debug_random_color(ivec2(tile.page.xy)), 0) * 0.5;
out_color_mul = vec4(0.5);
}

View File

@ -21,7 +21,7 @@ struct ShadowSampleParams {
float z_range;
};
ShadowTileData shadow_tile_data_get(usampler2D tilemaps_tx, ShadowSampleParams params)
ShadowSamplingTile shadow_tile_data_get(usampler2D tilemaps_tx, ShadowSampleParams params)
{
/* Prevent out of bound access. Assumes the input is already non negative. */
vec2 tilemap_uv = min(params.uv.xy, vec2(0.99999));
@ -46,9 +46,9 @@ float shadow_read_depth(SHADOW_ATLAS_TYPE atlas_tx,
const int page_shift = SHADOW_PAGE_LOD;
ivec2 tile_coord = texel_coord >> page_shift;
ShadowTileData tile = shadow_tile_load(tilemaps_tx, tile_coord, params.tilemap_index);
ShadowSamplingTile tile = shadow_tile_load(tilemaps_tx, tile_coord, params.tilemap_index);
if (!tile.is_allocated) {
if (!tile.is_valid) {
return -1.0;
}

View File

@ -26,7 +26,6 @@ void main()
ShadowTileData tile = shadow_tile_unpack(tiles_buf[tile_index]);
if (tile.is_used && !tile.is_allocated) {
shadow_page_alloc(tile);
tile.lod = lod;
tiles_buf[tile_index] = shadow_tile_pack(tile);
}

View File

@ -0,0 +1,93 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/**
* Virtual shadow-mapping: Amend sampling tile atlas.
*
* In order to support sampling different LOD for clipmap shadow projections, we need to scan
* through the LOD tilemaps from lowest LOD to highest LOD, gathering the last valid tile along the
* way for the current destination tile. For each new level we gather the previous level tiles from
* local memory using the correct relative offset from the previous level as they might not be
* aligned.
*
* TODO(fclem): This shader **should** be dispatched for one thread-group per directional light.
* Currently this shader is dispatched with one thread-group for all directional light.
*/
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_matrix_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
shared ShadowSamplingTilePacked tiles_local[SHADOW_TILEMAP_RES][SHADOW_TILEMAP_RES];
void main()
{
ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy);
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
LightData light = light_buf[l_idx];
/* This only works on clip-maps. Cascade have already the same LOD for every tile-maps. */
if (light.type != LIGHT_SUN) {
break;
}
ivec2 base_offset_neg = light_sun_data_get(light).clipmap_base_offset_neg;
ivec2 base_offset_pos = light_sun_data_get(light).clipmap_base_offset_pos;
/* LOD relative max with respect to clipmap_lod_min. */
int lod_max = light_sun_data_get(light).clipmap_lod_max -
light_sun_data_get(light).clipmap_lod_min;
/* Iterate in reverse. */
for (int lod = lod_max; lod >= 0; lod--) {
int tilemap_index = light.tilemap_index + lod;
ivec2 atlas_texel = shadow_tile_coord_in_atlas(tile_co, tilemap_index);
ShadowSamplingTilePacked tile_packed = imageLoad(tilemaps_img, atlas_texel).x;
ShadowSamplingTile tile = shadow_sampling_tile_unpack(tile_packed);
if (lod != lod_max && !tile.is_valid) {
/* Offset this LOD has with the previous one. In unit of tile of the current LOD. */
ivec2 offset_binary = ((base_offset_pos >> lod) & 1) - ((base_offset_neg >> lod) & 1);
ivec2 offset_centered = ivec2(SHADOW_TILEMAP_RES / 2) + offset_binary;
ivec2 tile_co_prev = (tile_co + offset_centered) >> 1;
/* Load tile from the previous LOD. */
ShadowSamplingTilePacked tile_prev_packed = tiles_local[tile_co_prev.y][tile_co_prev.x];
ShadowSamplingTile tile_prev = shadow_sampling_tile_unpack(tile_prev_packed);
/* We can only propagate LODs up to a certain level.
* Afterwards we run out of bits to store the offsets. */
if (tile_prev.is_valid && tile_prev.lod < SHADOW_TILEMAP_MAX_CLIPMAP_LOD - 1) {
/* Relative LOD. Used for reducing pixel rate at sampling time.
* Increase with each new invalid level. */
tile_prev.lod += 1;
/* The offset (in tile of current LOD) is equal to the offset from the bottom left corner
* of both LODs modulo the size of a tile of the source LOD (in tile of current LOD). */
/* Offset corner to center. */
tile_prev.lod_offset = uvec2(SHADOW_TILEMAP_RES / 2) << tile_prev.lod;
/* Align center of both LODs. */
tile_prev.lod_offset -= uvec2(SHADOW_TILEMAP_RES / 2);
/* Add the offset relative to the source LOD. */
tile_prev.lod_offset += uvec2(bitfieldExtract(base_offset_pos, lod, int(tile_prev.lod)) -
bitfieldExtract(base_offset_neg, lod, int(tile_prev.lod)));
/* Wrap to valid range. */
tile_prev.lod_offset &= ~(~0u << tile_prev.lod);
tile_prev_packed = shadow_sampling_tile_pack(tile_prev);
/* Replace the missing page with the one from the lower LOD. */
imageStore(tilemaps_img, atlas_texel, uvec4(tile_prev_packed));
/* Push this amended tile to the local tiles. */
tile_packed = tile_prev_packed;
tile.is_valid = true;
}
}
barrier();
tiles_local[tile_co.y][tile_co.x] = (tile.is_valid) ? tile_packed : SHADOW_NO_DATA;
barrier();
}
}
LIGHT_FOREACH_END
}

View File

@ -57,6 +57,7 @@ void main()
bool is_cubemap = (tilemap_data.projection_type == SHADOW_PROJECTION_CUBEFACE);
int lod_max = is_cubemap ? SHADOW_TILEMAP_LOD : 0;
int valid_tile_index = -1;
uint valid_lod = 0u;
/* With all threads (LOD0 size dispatch) load each lod tile from the highest lod
* to the lowest, keeping track of the lowest one allocated which will be use for shadowing.
* This guarantee a O(1) lookup time.
@ -176,12 +177,17 @@ void main()
if (tile.is_used && tile.is_allocated && (!tile.do_update || lod_is_rendered)) {
/* Save highest lod for this thread. */
valid_tile_index = tile_index;
valid_lod = uint(lod);
}
}
/* Store the highest LOD valid page for rendering. */
uint tile_packed = (valid_tile_index != -1) ? tiles_buf[valid_tile_index] : SHADOW_NO_DATA;
imageStore(tilemaps_img, atlas_texel, uvec4(tile_packed));
ShadowTileDataPacked tile_packed = (valid_tile_index != -1) ? tiles_buf[valid_tile_index] :
SHADOW_NO_DATA;
ShadowTileData tile_data = shadow_tile_unpack(tile_packed);
ShadowSamplingTile tile_sampling = shadow_sampling_tile_create(tile_data, valid_lod);
ShadowSamplingTilePacked tile_sampling_packed = shadow_sampling_tile_pack(tile_sampling);
imageStore(tilemaps_img, atlas_texel, uvec4(tile_sampling_packed));
if (all(equal(gl_GlobalInvocationID, uvec3(0)))) {
/* Clamp it as it can underflow if there is too much tile present on screen. */

View File

@ -91,14 +91,14 @@ int shadow_tile_offset(ivec2 tile, int tiles_index, int lod)
* \{ */
/** \note: Will clamp if out of bounds. */
ShadowTileData shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int tilemap_index)
ShadowSamplingTile shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int tilemap_index)
{
/* NOTE(@fclem): This clamp can hide some small imprecision at clip-map transition.
* Can be disabled to check if the clip-map is well centered. */
tile_co = clamp(tile_co, ivec2(0), ivec2(SHADOW_TILEMAP_RES - 1));
uint tile_data =
texelFetch(tilemaps_tx, shadow_tile_coord_in_atlas(tile_co, tilemap_index), 0).x;
return shadow_tile_unpack(tile_data);
ivec2 texel = shadow_tile_coord_in_atlas(tile_co, tilemap_index);
uint tile_data = texelFetch(tilemaps_tx, texel, 0).x;
return shadow_sampling_tile_unpack(tile_data);
}
/**

View File

@ -23,16 +23,19 @@ float shadow_read_depth_at_tilemap_uv(int tilemap_index, vec2 tilemap_uv)
ivec2 texel_coord = ivec2(tilemap_uv * float(SHADOW_MAP_MAX_RES));
/* Using bitwise ops is way faster than integer ops. */
const int page_shift = SHADOW_PAGE_LOD;
const int page_mask = ~(0xFFFFFFFF << SHADOW_PAGE_LOD);
ivec2 tile_coord = texel_coord >> page_shift;
ShadowTileData tile = shadow_tile_load(shadow_tilemaps_tx, tile_coord, tilemap_index);
ShadowSamplingTile tile = shadow_tile_load(shadow_tilemaps_tx, tile_coord, tilemap_index);
if (!tile.is_allocated) {
if (!tile.is_valid) {
return -1.0;
}
int page_mask = ~(0xFFFFFFFF << (SHADOW_PAGE_LOD + int(tile.lod)));
ivec2 texel_page = (texel_coord & page_mask) >> int(tile.lod);
/* Shift LOD0 pixels so that they get wrapped at the right position for the given LOD. */
/* TODO convert everything to uint to avoid signed int operations. */
texel_coord += ivec2(tile.lod_offset << SHADOW_PAGE_LOD);
/* Scale to LOD pixels (merge LOD0 pixels together) then mask to get pixel in page. */
ivec2 texel_page = (texel_coord >> int(tile.lod)) & page_mask;
ivec3 texel = ivec3((ivec2(tile.page.xy) << page_shift) | texel_page, tile.page.z);
return uintBitsToFloat(texelFetch(shadow_atlas_tx, texel, 0).r);
@ -439,8 +442,8 @@ vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3
else {
params = shadow_punctual_sample_params_get(light, P);
}
ShadowTileData tile = shadow_tile_data_get(shadow_tilemaps_tx, params);
if (!tile.is_allocated) {
ShadowSamplingTile tile = shadow_tile_data_get(shadow_tilemaps_tx, params);
if (!tile.is_valid) {
return vec3(0.0);
}

View File

@ -195,6 +195,13 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tilemap_finalize)
.additional_info("eevee_shared")
.compute_source("eevee_shadow_tilemap_finalize_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_shadow_tilemap_amend)
.do_static_compilation(true)
.local_group_size(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES)
.image(0, GPU_R32UI, Qualifier::READ_WRITE, ImageType::UINT_2D, "tilemaps_img")
.additional_info("eevee_shared", "eevee_light_data", "draw_view")
.compute_source("eevee_shadow_tilemap_amend_comp.glsl");
/* AtomicMin clear implementation. */
GPU_SHADER_CREATE_INFO(eevee_shadow_page_clear)
.do_static_compilation(true)

View File

@ -639,22 +639,23 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
vcount = 0;
};
const auto drawcall_add = [&](blender::gpu::Batch *draw_geom, int v_first, int v_count) {
const auto drawcall_add =
[&](blender::gpu::Batch *draw_geom, const int v_first, const int v_count) {
#if DISABLE_BATCHING
DRW_shgroup_call_range(grp, ob, geom, v_first, v_count);
return;
DRW_shgroup_call_range(grp, ob, geom, v_first, v_count);
return;
#endif
int last = vfirst + vcount;
/* Interrupt draw-call grouping if the sequence is not consecutive. */
if ((draw_geom != iter_geom) || (v_first - last > 0)) {
drawcall_flush();
}
iter_geom = draw_geom;
if (vfirst == -1) {
vfirst = v_first;
}
vcount = v_first + v_count - vfirst;
};
int last = vfirst + vcount;
/* Interrupt draw-call grouping if the sequence is not consecutive. */
if ((draw_geom != iter_geom) || (v_first - last > 0)) {
drawcall_flush();
}
iter_geom = draw_geom;
if (vfirst == -1) {
vfirst = v_first;
}
vcount = v_first + v_count - vfirst;
};
int t_offset = 0;
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*pd->scene, grease_pencil);
@ -701,7 +702,7 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
visible_strokes.foreach_index([&](const int stroke_i) {
const IndexRange points = points_by_curve[stroke_i];
const int material_index = stroke_materials[stroke_i];
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1);
const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1);
const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
const bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0);
@ -736,9 +737,9 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
gpencil_material_resources_get(
matpool, mat_ofs + material_index, &new_tex_stroke, &new_tex_fill, &new_ubo_mat);
bool resource_changed = (ubo_mat != new_ubo_mat) ||
(new_tex_fill && (new_tex_fill != tex_fill)) ||
(new_tex_stroke && (new_tex_stroke != tex_stroke));
const bool resource_changed = (ubo_mat != new_ubo_mat) ||
(new_tex_fill && (new_tex_fill != tex_fill)) ||
(new_tex_stroke && (new_tex_stroke != tex_stroke));
if (resource_changed) {
drawcall_flush();
@ -771,16 +772,16 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
}
if (show_fill) {
int v_first = t_offset * 3;
int v_count = num_stroke_triangles * 3;
const int v_first = t_offset * 3;
const int v_count = num_stroke_triangles * 3;
drawcall_add(geom, v_first, v_count);
}
t_offset += num_stroke_triangles;
if (show_stroke) {
int v_first = t_offset * 3;
int v_count = num_stroke_vertices * 2 * 3;
const int v_first = t_offset * 3;
const int v_count = num_stroke_vertices * 2 * 3;
drawcall_add(geom, v_first, v_count);
}

View File

@ -76,7 +76,7 @@ PassMain::Sub &MeshPass::get_subpass(
sub_pass->bind_texture(WB_TEXTURE_SLOT, gputex.texture, sampler_state);
}
sub_pass->push_constant("isImageTile", gputex.tile_mapping != nullptr);
sub_pass->push_constant("imagePremult", image && image->alpha_mode == IMA_ALPHA_PREMUL);
sub_pass->push_constant("imagePremult", image->alpha_mode == IMA_ALPHA_PREMUL);
/* TODO(@pragma37): This setting should be exposed on the user side,
* either as a global parameter (and set it here)
* or by reading the Material Clipping Threshold (and set it per material) */

View File

@ -538,6 +538,10 @@ static void grease_pencil_geom_batch_ensure(Object &object,
"fill_color", bke::AttrDomain::Curve, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
const VArray<int> materials = *attributes.lookup_or_default<int>(
"material_index", bke::AttrDomain::Curve, 0);
const VArray<float> u_translations = *attributes.lookup_or_default<float>(
"u_translation", bke::AttrDomain::Curve, 0.0f);
const VArray<float> u_scales = *attributes.lookup_or_default<float>(
"u_scale", bke::AttrDomain::Curve, 1.0f);
const Span<uint3> triangles = info.drawing.triangles();
const Span<float4x2> texture_matrices = info.drawing.texture_matrices();
const Span<int> verts_start_offsets = verts_start_offsets_per_visible_drawing[drawing_i];
@ -554,7 +558,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
int8_t end_cap,
int point_i,
int idx,
float length,
float u_stroke,
const float4x2 &texture_matrix,
GreasePencilStrokeVert &s_vert,
GreasePencilColorVert &c_vert) {
@ -572,7 +576,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness(
rotations[point_i], stroke_point_aspect_ratios[curve_i], stroke_hardnesses[curve_i]);
s_vert.u_stroke = length;
s_vert.u_stroke = u_stroke;
copy_v2_v2(s_vert.uv_fill, texture_matrix * float4(pos, 1.0f));
copy_v4_v4(c_vert.vcol, vertex_colors[point_i]);
@ -585,12 +589,12 @@ static void grease_pencil_geom_batch_ensure(Object &object,
};
visible_strokes.foreach_index([&](const int curve_i, const int pos) {
IndexRange points = points_by_curve[curve_i];
const IndexRange points = points_by_curve[curve_i];
const bool is_cyclic = cyclic[curve_i];
const int verts_start_offset = verts_start_offsets[pos];
const int tris_start_offset = tris_start_offsets[pos];
const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
const IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
const float4x2 texture_matrix = texture_matrices[curve_i] * object_space_to_layer_space;
@ -612,31 +616,33 @@ static void grease_pencil_geom_batch_ensure(Object &object,
}
/* Write all the point attributes to the vertex buffers. Create a quad for each point. */
const float u_scale = u_scales[curve_i];
const float u_translation = u_translations[curve_i];
for (const int i : IndexRange(points.size())) {
const int idx = i + 1;
const float length = (i >= 1) ? lengths[i - 1] : 0.0f;
const float u_stroke = u_scale * (i > 0 ? lengths[i - 1] : 0.0f) + u_translation;
populate_point(verts_range,
curve_i,
start_caps[curve_i],
end_caps[curve_i],
points[i],
idx,
length,
u_stroke,
texture_matrix,
verts_slice[idx],
cols_slice[idx]);
}
if (is_cyclic) {
if (is_cyclic && points.size() > 1) {
const int idx = points.size() + 1;
const float length = points.size() > 1 ? lengths[points.size() - 1] : 0.0f;
const float u_stroke = u_scale * lengths[points.size() - 1] + u_translation;
populate_point(verts_range,
curve_i,
start_caps[curve_i],
end_caps[curve_i],
points[0],
idx,
length,
u_stroke,
texture_matrix,
verts_slice[idx],
cols_slice[idx]);

View File

@ -71,7 +71,7 @@ static void extract_fdots_nor_finish(const MeshRenderData &mr,
nor[f].w = NOR_AND_FLAG_HIDDEN;
}
else {
nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
nor[f] = GPU_normal_convert_i10_v3(mr.face_normals[f]);
/* Select / Active Flag. */
nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
((efa == mr.efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :

View File

@ -120,7 +120,6 @@ static void extract_tan_init_common(const MeshRenderData &mr,
&tangent_mask);
}
else {
/* TODO: This is not thread-safe. Draw extraction should not modify the mesh. */
BKE_mesh_calc_loop_tangent_ex(reinterpret_cast<const float(*)[3]>(mr.vert_positions.data()),
mr.faces,
mr.corner_verts.data(),

View File

@ -10,7 +10,7 @@
#include "BKE_node.hh"
#include "BKE_object.hh"
#include "DEG_depsgraph.hh"
#include "BLI_vector.hh"
#include "RNA_define.hh"
@ -1164,6 +1164,375 @@ static void test_eevee_shadow_finalize()
}
DRAW_TEST(eevee_shadow_finalize)
static void test_eevee_shadow_tile_packing()
{
Vector<uint> test_values{0x00000000u, 0x00000001u, 0x0000000Fu, 0x000000FFu, 0xABCDEF01u,
0xAAAAAAAAu, 0xBBBBBBBBu, 0xCCCCCCCCu, 0xDDDDDDDDu, 0xEEEEEEEEu,
0xFFFFFFFFu, 0xDEADBEEFu, 0x8BADF00Du, 0xABADCAFEu, 0x0D15EA5Eu,
0xFEE1DEADu, 0xDEADC0DEu, 0xC00010FFu, 0xBBADBEEFu, 0xBAAAAAADu};
for (auto value : test_values) {
EXPECT_EQ(shadow_page_unpack(value),
shadow_page_unpack(shadow_page_pack(shadow_page_unpack(value))));
EXPECT_EQ(shadow_lod_offset_unpack(value),
shadow_lod_offset_unpack(shadow_lod_offset_pack(shadow_lod_offset_unpack(value))));
ShadowTileData expected_tile = shadow_tile_unpack(value);
ShadowTileData result_tile = shadow_tile_unpack(shadow_tile_pack(expected_tile));
EXPECT_EQ(expected_tile.page, result_tile.page);
EXPECT_EQ(expected_tile.cache_index, result_tile.cache_index);
EXPECT_EQ(expected_tile.is_used, result_tile.is_used);
EXPECT_EQ(expected_tile.do_update, result_tile.do_update);
EXPECT_EQ(expected_tile.is_allocated, result_tile.is_allocated);
EXPECT_EQ(expected_tile.is_rendered, result_tile.is_rendered);
EXPECT_EQ(expected_tile.is_cached, result_tile.is_cached);
ShadowSamplingTile expected_sampling_tile = shadow_sampling_tile_unpack(value);
ShadowSamplingTile result_sampling_tile = shadow_sampling_tile_unpack(
shadow_sampling_tile_pack(expected_sampling_tile));
EXPECT_EQ(expected_sampling_tile.page, result_sampling_tile.page);
EXPECT_EQ(expected_sampling_tile.lod, result_sampling_tile.lod);
EXPECT_EQ(expected_sampling_tile.lod_offset, result_sampling_tile.lod_offset);
EXPECT_EQ(expected_sampling_tile.is_valid, result_sampling_tile.is_valid);
}
}
DRAW_TEST(eevee_shadow_tile_packing)
static void test_eevee_shadow_tilemap_amend()
{
GPU_render_begin();
blender::Vector<uint32_t> tilemap_data(SHADOW_TILEMAP_RES * SHADOW_TILEMAP_RES *
SHADOW_TILEMAP_PER_ROW);
tilemap_data.fill(0);
auto pixel_get = [&](int x, int y, int tilemap_index) -> uint32_t & {
/* Note: assumes that tilemap_index is < SHADOW_TILEMAP_PER_ROW. */
return tilemap_data[y * SHADOW_TILEMAP_RES * SHADOW_TILEMAP_PER_ROW + x +
tilemap_index * SHADOW_TILEMAP_RES];
};
ShadowSamplingTile tile;
tile.lod = 0;
tile.lod_offset = uint2(0);
tile.is_valid = true;
tile.page = uint3(1, 0, 0);
pixel_get(16, 16, 2) = shadow_sampling_tile_pack(tile);
tile.page = uint3(2, 0, 0);
pixel_get(17, 16, 2) = shadow_sampling_tile_pack(tile);
tile.page = uint3(3, 0, 0);
pixel_get(20, 20, 1) = shadow_sampling_tile_pack(tile);
tile.page = uint3(4, 0, 0);
pixel_get(17, 16, 0) = shadow_sampling_tile_pack(tile);
Texture tilemap_tx = {"tilemap_tx"};
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_HOST_READ | GPU_TEXTURE_USAGE_SHADER_READ |
GPU_TEXTURE_USAGE_SHADER_WRITE;
int2 tilemap_res(SHADOW_TILEMAP_RES * SHADOW_TILEMAP_PER_ROW, SHADOW_TILEMAP_RES);
tilemap_tx.ensure_2d(GPU_R32UI, tilemap_res, usage);
GPU_texture_update_sub(
tilemap_tx, GPU_DATA_UINT, tilemap_data.data(), 0, 0, 0, tilemap_res.x, tilemap_res.y, 0);
/* Setup one directional light with 3 tilemaps. Fill only the needed data. */
LightData light;
light.type = LIGHT_SUN;
light.sun.clipmap_lod_min = 0;
light.sun.clipmap_lod_max = 2;
/* Shift LOD0 by 1 tile towards bottom. */
light.sun.clipmap_base_offset_neg = int2(0, 1 << 0);
/* Shift LOD1 by 1 tile towards right. */
light.sun.clipmap_base_offset_pos = int2(1 << 1, 0);
light.tilemap_index = 0;
LightDataBuf culling_light_buf = {"Lights_culled"};
culling_light_buf[0] = light;
culling_light_buf.push_update();
LightCullingDataBuf culling_data_buf = {"LightCull_data"};
culling_data_buf.local_lights_len = 0;
culling_data_buf.sun_lights_len = 1;
culling_data_buf.items_count = 1;
culling_data_buf.push_update();
/* Needed for validation. But not used since we use directionals. */
LightCullingZbinBuf culling_zbin_buf = {"LightCull_zbin"};
LightCullingTileBuf culling_tile_buf = {"LightCull_tile"};
GPUShader *sh = GPU_shader_create_from_info_name("eevee_shadow_tilemap_amend");
PassSimple pass("Test");
pass.shader_set(sh);
pass.bind_image("tilemaps_img", tilemap_tx);
pass.bind_ssbo(LIGHT_CULL_BUF_SLOT, culling_data_buf);
pass.bind_ssbo(LIGHT_BUF_SLOT, culling_light_buf);
pass.bind_ssbo(LIGHT_ZBIN_BUF_SLOT, culling_zbin_buf);
pass.bind_ssbo(LIGHT_TILE_BUF_SLOT, culling_tile_buf);
pass.dispatch(int3(1));
pass.barrier(GPU_BARRIER_TEXTURE_UPDATE);
Manager manager;
manager.submit(pass);
{
uint *pixels = tilemap_tx.read<uint32_t>(GPU_DATA_UINT);
auto stringify_tilemap = [&](int tilemap_index) -> std::string {
std::string result = "";
for (auto y : IndexRange(SHADOW_TILEMAP_RES)) {
for (auto x : IndexRange(SHADOW_TILEMAP_RES)) {
/* Note: assumes that tilemap_index is < SHADOW_TILEMAP_PER_ROW. */
int tile_ofs = y * SHADOW_TILEMAP_RES * SHADOW_TILEMAP_PER_ROW + x +
tilemap_index * SHADOW_TILEMAP_RES;
ShadowSamplingTile tile = shadow_sampling_tile_unpack(pixels[tile_ofs]);
result += std::to_string(tile.page.x + tile.page.y * SHADOW_PAGE_PER_ROW);
if (x + 1 == SHADOW_TILEMAP_RES / 2) {
result += " ";
}
}
result += "\n";
if (y + 1 == SHADOW_TILEMAP_RES / 2) {
result += "\n";
}
}
return result;
};
auto stringify_lod = [&](int tilemap_index) -> std::string {
std::string result = "";
for (auto y : IndexRange(SHADOW_TILEMAP_RES)) {
for (auto x : IndexRange(SHADOW_TILEMAP_RES)) {
/* Note: assumes that tilemap_index is < SHADOW_TILEMAP_PER_ROW. */
int tile_ofs = y * SHADOW_TILEMAP_RES * SHADOW_TILEMAP_PER_ROW + x +
tilemap_index * SHADOW_TILEMAP_RES;
ShadowSamplingTile tile = shadow_sampling_tile_unpack(pixels[tile_ofs]);
result += std::to_string(tile.lod);
if (x + 1 == SHADOW_TILEMAP_RES / 2) {
result += " ";
}
}
result += "\n";
if (y + 1 == SHADOW_TILEMAP_RES / 2) {
result += "\n";
}
}
return result;
};
auto stringify_offset = [&](int tilemap_index) -> std::string {
std::string result = "";
for (auto y : IndexRange(SHADOW_TILEMAP_RES)) {
for (auto x : IndexRange(SHADOW_TILEMAP_RES)) {
/* Note: assumes that tilemap_index is < SHADOW_TILEMAP_PER_ROW. */
int tile_ofs = y * SHADOW_TILEMAP_RES * SHADOW_TILEMAP_PER_ROW + x +
tilemap_index * SHADOW_TILEMAP_RES;
ShadowSamplingTile tile = shadow_sampling_tile_unpack(pixels[tile_ofs]);
result += std::to_string(tile.lod_offset.x + tile.lod_offset.y);
if (x + 1 == SHADOW_TILEMAP_RES / 2) {
result += " ";
}
}
result += "\n";
if (y + 1 == SHADOW_TILEMAP_RES / 2) {
result += "\n";
}
}
return result;
};
/** The layout of these expected strings is Y down. */
StringRefNull expected_pages_lod2 =
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"\n"
"0000000000000000 1200000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n";
StringRefNull expected_pages_lod1 =
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"\n"
"0000000000000001 1220000000000000\n"
"0000000000000001 1220000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000300000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n";
StringRefNull expected_pages_lod0 =
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"\n"
"0000000000000000 0400000000000000\n"
"0000000000000011 1122220000000000\n"
"0000000000000011 1122220000000000\n"
"0000000000000011 1122220000000000\n"
"0000000000000011 1122220000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000033000000\n"
"0000000000000000 0000000033000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n";
EXPECT_EQ(expected_pages_lod2, stringify_tilemap(2));
EXPECT_EQ(expected_pages_lod1, stringify_tilemap(1));
EXPECT_EQ(expected_pages_lod0, stringify_tilemap(0));
StringRefNull expected_lod_lod0 =
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"\n"
"0000000000000000 0000000000000000\n"
"0000000000000022 2222220000000000\n"
"0000000000000022 2222220000000000\n"
"0000000000000022 2222220000000000\n"
"0000000000000022 2222220000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000011000000\n"
"0000000000000000 0000000011000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n";
EXPECT_EQ(expected_lod_lod0, stringify_lod(0));
/* Offset for each axis are added together in this test. */
StringRefNull expected_offset_lod0 =
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"\n"
"0000000000000000 0000000000000000\n"
"0000000000000055 5555550000000000\n"
"0000000000000055 5555550000000000\n"
"0000000000000055 5555550000000000\n"
"0000000000000055 5555550000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000011000000\n"
"0000000000000000 0000000011000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n"
"0000000000000000 0000000000000000\n";
EXPECT_EQ(expected_offset_lod0, stringify_offset(0));
MEM_SAFE_FREE(pixels);
}
GPU_shader_free(sh);
DRW_shaders_free();
GPU_render_end();
}
DRAW_TEST(eevee_shadow_tilemap_amend)
static void test_eevee_shadow_page_mask_ex(int max_view_per_tilemap)
{
GPU_render_begin();

View File

@ -10,6 +10,7 @@ set(INC
../../makesrna
../../sequencer
../../windowmanager
../../../../extern/fmtlib/include
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)

View File

@ -342,10 +342,10 @@ float get_default_rna_value(FCurve *fcu, PropertyRNA *prop, PointerRNA *ptr)
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN:
if (len) {
default_value = RNA_property_boolean_get_default_index(ptr, prop, fcu->array_index);
default_value = float(RNA_property_boolean_get_default_index(ptr, prop, fcu->array_index));
}
else {
default_value = RNA_property_boolean_get_default(ptr, prop);
default_value = float(RNA_property_boolean_get_default(ptr, prop));
}
break;
case PROP_INT:

View File

@ -9,6 +9,8 @@
#include <cstddef>
#include <cstdio>
#include <fmt/format.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
@ -298,9 +300,9 @@ static blender::Vector<std::string> construct_rna_paths(PointerRNA *ptr)
if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES) {
if (properties) {
LISTBASE_FOREACH (IDProperty *, prop, &properties->data.group) {
std::string name = prop->name;
std::string rna_path = "[\"" + name + "\"]";
paths.append(rna_path);
char name_escaped[MAX_IDPROP_NAME * 2];
BLI_str_escape(name_escaped, prop->name, sizeof(name_escaped));
paths.append(fmt::format("[\"{}\"]", name_escaped));
}
}
}

View File

@ -539,10 +539,10 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
if (UNLIKELY(uint(fcu->array_index) >= RNA_property_array_length(&ptr, prop))) {
break; /* Out of range, skip. */
}
tval = RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
tval = float(RNA_property_boolean_get_index(&ptr, prop, fcu->array_index));
}
else {
tval = RNA_property_boolean_get(&ptr, prop);
tval = float(RNA_property_boolean_get(&ptr, prop));
}
pose_slide_apply_val(pso, fcu, pfl->ob, &tval);

View File

@ -223,7 +223,7 @@ static void gizmo_primitive_setup(wmGizmo *gz)
/* Default Values. */
PrimitiveGizmo3D *gz_prim = (PrimitiveGizmo3D *)gz;
gz_prim->draw_style = ED_GIZMO_PRIMITIVE_STYLE_PLANE;
gz_prim->arc_inner_factor = true;
gz_prim->arc_inner_factor = 1.0f;
gz_prim->draw_inner = true;
}

View File

@ -1420,7 +1420,7 @@ static bool gpencil_stroke_eraser_is_occluded(
if (brush->gpencil_tool == GPAINT_TOOL_ERASE) {
gp_settings = brush->gpencil_settings;
}
else if ((eraser != nullptr) & (eraser->gpencil_tool == GPAINT_TOOL_ERASE)) {
else if ((eraser != nullptr) && (eraser->gpencil_tool == GPAINT_TOOL_ERASE)) {
gp_settings = eraser->gpencil_settings;
}

View File

@ -2515,10 +2515,10 @@ double ui_but_value_get(uiBut *but)
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN:
if (RNA_property_array_check(prop)) {
value = RNA_property_boolean_get_index(&but->rnapoin, prop, but->rnaindex);
value = double(RNA_property_boolean_get_index(&but->rnapoin, prop, but->rnaindex));
}
else {
value = RNA_property_boolean_get(&but->rnapoin, prop);
value = double(RNA_property_boolean_get(&but->rnapoin, prop));
}
break;
case PROP_INT:
@ -3110,7 +3110,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str)
{
RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr, nullptr);
}
else if (search_but->item_active != nullptr) {
else if (search_but && search_but->item_active != nullptr) {
rptr = RNA_pointer_create(nullptr,
RNA_property_pointer_type(&but->rnapoin, but->rnaprop),
search_but->item_active);

View File

@ -2275,7 +2275,7 @@ void uiItemFullR(uiLayout *layout,
char str[2] = {'\0'};
for (int a = 0; a < len; a++) {
str[0] = RNA_property_array_item_char(prop, a);
const bool use_prefix = (a == 0 && name && name[0]);
const bool use_prefix = (a == 0 && name[0]);
if (use_prefix) {
char *s = name_with_suffix;
s += STRNCPY_RLEN(name_with_suffix, name);
@ -2302,14 +2302,11 @@ void uiItemFullR(uiLayout *layout,
}
}
else {
if (name) {
but = uiDefBut(
block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
but->drawflag |= UI_BUT_TEXT_RIGHT;
but->drawflag &= ~UI_BUT_TEXT_LEFT;
but = uiDefBut(block, UI_BTYPE_LABEL, 0, name, 0, 0, w, UI_UNIT_Y, nullptr, 0.0, 0.0, "");
but->drawflag |= UI_BUT_TEXT_RIGHT;
but->drawflag &= ~UI_BUT_TEXT_LEFT;
label_added = true;
}
label_added = true;
}
if (!label_added && heading_layout) {

View File

@ -409,7 +409,7 @@ void UI_popover_end(bContext *C, uiPopover *pup, wmKeyMap *keymap)
pup->window = window;
/* TODO(@ideasman42): we may want to make this configurable.
* The begin/end stype of calling popups doesn't allow 'can_refresh' to be set.
* The begin/end type of calling popups doesn't allow 'can_refresh' to be set.
* For now close this style of popovers when accessed. */
UI_block_flag_disable(pup->block, UI_BLOCK_KEEP_OPEN);
}

View File

@ -561,7 +561,7 @@ static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle)
CTX_wm_window_set(C, win);
ui_region_temp_remove(C, screen, handle->region);
/* Reset context (area and region were nullptr'ed when changing context window). */
/* Reset context (area and region were null'ed when changing context window). */
CTX_wm_window_set(C, ctx_win);
CTX_wm_area_set(C, ctx_area);
CTX_wm_region_set(C, ctx_region);

View File

@ -5685,7 +5685,7 @@ static void join_triangle_props(wmOperatorType *ot)
RNA_def_property_float_default(prop, DEG2RADF(40.0f));
RNA_def_boolean(ot->srna, "uvs", false, "Compare UVs", "");
RNA_def_boolean(ot->srna, "vcols", false, "Compare VCols", "");
RNA_def_boolean(ot->srna, "vcols", false, "Compare Color Attributes", "");
RNA_def_boolean(ot->srna, "seam", false, "Compare Seam", "");
RNA_def_boolean(ot->srna, "sharp", false, "Compare Sharp", "");
RNA_def_boolean(ot->srna, "materials", false, "Compare Materials", "");

View File

@ -839,7 +839,7 @@ static Vector<NodeBakeRequest> bake_single_node_gather_bake_request(bContext *C,
}
request.path = std::move(*bake_path);
if (bake->bake_mode == NODES_MODIFIER_BAKE_MODE_STILL) {
if (node->type == GEO_NODE_BAKE && bake->bake_mode == NODES_MODIFIER_BAKE_MODE_STILL) {
const int current_frame = scene->r.cfra;
request.frame_start = current_frame;
request.frame_end = current_frame;

View File

@ -110,7 +110,7 @@ void FLUID_OT_bake_guides(wmOperatorType *ot);
void FLUID_OT_free_guides(wmOperatorType *ot);
void FLUID_OT_pause_bake(wmOperatorType *ot);
/* dynamicpaint.cc */
/* `dynamicpaint.cc` */
void DPAINT_OT_bake(wmOperatorType *ot);
/**

View File

@ -3219,7 +3219,7 @@ void ED_region_panels_layout_ex(const bContext *C,
}
const int width = panel_draw_width_from_max_width_get(region, panel->type, max_panel_width);
if (panel && UI_panel_is_dragging(panel)) {
if (UI_panel_is_dragging(panel)) {
/* Prevent View2d.tot rectangle size changes while dragging panels. */
update_tot_size = false;
}

View File

@ -61,7 +61,7 @@ enum eScreenAxis {
*/
#define BORDERPADDING ((2.0f * UI_SCALE_FAC) + U.pixelsize)
/* area.cc */
/* `area.cc` */
/**
* We swap spaces for full-screen to keep all allocated data area vertices were set.
@ -71,7 +71,7 @@ void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src);
/* for quick toggle, can skip fades */
void region_toggle_hidden(bContext *C, ARegion *region, bool do_fade);
/* screen_draw.cc */
/* `screen_draw.cc` */
/**
* Visual indication of the two areas involved in a proposed join.
@ -82,7 +82,7 @@ void region_toggle_hidden(bContext *C, ARegion *region, bool do_fade);
void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2);
void screen_draw_split_preview(ScrArea *area, eScreenAxis dir_axis, float fac);
/* screen_edit.cc */
/* `screen_edit.cc` */
/**
* Empty screen, with 1 dummy area without space-data. Uses window size.
@ -127,7 +127,7 @@ bool screen_area_close(bContext *C, bScreen *screen, ScrArea *area);
void screen_area_spacelink_add(const Scene *scene, ScrArea *area, eSpace_Type space_type);
AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]);
/* screen_geometry.cc */
/* `screen_geometry.cc` */
int screen_geom_area_height(const ScrArea *area);
int screen_geom_area_width(const ScrArea *area);
@ -171,7 +171,7 @@ short screen_geom_find_area_split_point(const ScrArea *area,
*/
void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge);
/* screen_context.cc */
/* `screen_context.cc` */
/**
* Entry point for the screen context.
@ -180,11 +180,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
extern "C" const char *screen_context_dir[]; /* doc access */
/* screendump.cc */
/* `screendump.cc` */
void SCREEN_OT_screenshot(wmOperatorType *ot);
void SCREEN_OT_screenshot_area(wmOperatorType *ot);
/* workspace_layout_edit.cc */
/* `workspace_layout_edit.cc` */
bool workspace_layout_set_poll(const WorkSpaceLayout *layout);

View File

@ -42,8 +42,8 @@ set(SRC
curves_sculpt_snake_hook.cc
grease_pencil_draw_ops.cc
grease_pencil_erase.cc
grease_pencil_tint.cc
grease_pencil_paint.cc
grease_pencil_tint.cc
paint_canvas.cc
paint_cursor.cc
paint_curve.cc

View File

@ -605,7 +605,7 @@ void PaintOperation::process_stroke_end(const bContext &C, bke::greasepencil::Dr
{
Scene *scene = CTX_data_scene(&C);
const int stroke_index = drawing.strokes().curves_range().last();
const IndexRange points = drawing.strokes().points_by_curve()[stroke_index];
IndexRange points = drawing.strokes().points_by_curve()[stroke_index];
bke::CurvesGeometry &curves = drawing.strokes_for_write();
const VArray<float> radii = drawing.radii();
@ -622,6 +622,7 @@ void PaintOperation::process_stroke_end(const bContext &C, bke::greasepencil::Dr
if (points_to_remove > 0) {
curves.resize(curves.points_num() - points_to_remove, curves.curves_num());
curves.offsets_for_write().last() = curves.points_num();
points = points.drop_back(points_to_remove);
}
const bke::AttrDomain selection_domain = ED_grease_pencil_selection_domain_get(

View File

@ -104,8 +104,9 @@ void TintOperation::on_stroke_begin(const bContext &C, const InputSample & /*sta
screen_positions_per_drawing_.reinitialize(drawings_.size());
threading::parallel_for_each(drawings_.index_range(), [&](const int drawing_index) {
MutableDrawingInfo drawing_info = drawings_[drawing_index];
threading::parallel_for_each(drawings_, [&](const MutableDrawingInfo &drawing_info) {
const int drawing_index = (&drawing_info - drawings_.data());
bke::CurvesGeometry &strokes = drawing_info.drawing.strokes_for_write();
const Layer &layer = *grease_pencil.layers()[drawing_info.layer_index];
@ -244,8 +245,8 @@ void TintOperation::execute_tint(const bContext &C, const InputSample &extension
fill_colors.finish();
};
threading::parallel_for_each(drawings_.index_range(), [&](const int drawing_index) {
const MutableDrawingInfo &info = drawings_[drawing_index];
threading::parallel_for_each(drawings_, [&](const MutableDrawingInfo &info) {
const int drawing_index = (&info - drawings_.data());
execute_tint_on_drawing(info.drawing, drawing_index);
});

View File

@ -641,17 +641,55 @@ static void operator_properties(wmOperatorType *ot)
RNA_def_enum(ot->srna, "trim_solver", solver_modes, int(SolverMode::Fast), "Solver", nullptr);
}
static int gesture_box_exec(bContext *C, wmOperator *op)
static bool can_invoke(const bContext &C)
{
Object *object = CTX_data_active_object(C);
SculptSession *ss = object->sculpt;
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
/* Not supported in Multires and Dyntopo. */
return OPERATOR_CANCELLED;
const View3D &v3d = *CTX_wm_view3d(&C);
const Base &base = *CTX_data_active_base(&C);
if (!BKE_base_is_visible(&v3d, &base)) {
return false;
}
if (ss->totvert == 0) {
return true;
}
static bool can_exec(const bContext &C)
{
const Object &object = *CTX_data_active_object(&C);
const SculptSession &ss = *object.sculpt;
if (BKE_pbvh_type(ss.pbvh) != PBVH_FACES) {
/* Not supported in Multires and Dyntopo. */
return false;
}
if (ss.totvert == 0) {
/* No geometry to trim or to detect a valid position for the trimming shape. */
return false;
}
return true;
}
static void initialize_cursor_info(bContext &C, const wmEvent *event)
{
const Object &ob = *CTX_data_active_object(&C);
SculptSession &ss = *ob.sculpt;
SCULPT_vertex_random_access_ensure(&ss);
SculptCursorGeometryInfo sgi;
const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
/* TODO: Remove gesture_* properties from SculptSession */
ss.gesture_initial_hit = SCULPT_cursor_geometry_info_update(&C, &sgi, mval_fl, false);
if (ss.gesture_initial_hit) {
copy_v3_v3(ss.gesture_initial_location, sgi.location);
copy_v3_v3(ss.gesture_initial_normal, sgi.normal);
}
}
static int gesture_box_exec(bContext *C, wmOperator *op)
{
if (!can_exec(*C)) {
return OPERATOR_CANCELLED;
}
@ -667,42 +705,18 @@ static int gesture_box_exec(bContext *C, wmOperator *op)
static int gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
const View3D *v3d = CTX_wm_view3d(C);
const Base *base = CTX_data_active_base(C);
if (!BKE_base_is_visible(v3d, base)) {
if (!can_invoke(*C)) {
return OPERATOR_CANCELLED;
}
SculptCursorGeometryInfo sgi;
const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
SCULPT_vertex_random_access_ensure(ss);
ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
if (ss->gesture_initial_hit) {
copy_v3_v3(ss->gesture_initial_location, sgi.location);
copy_v3_v3(ss->gesture_initial_normal, sgi.normal);
}
initialize_cursor_info(*C, event);
return WM_gesture_box_invoke(C, op, event);
}
static int gesture_lasso_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *object = CTX_data_active_object(C);
BKE_sculpt_update_object_for_edit(depsgraph, object, false);
SculptSession *ss = object->sculpt;
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
/* Not supported in Multires and Dyntopo. */
return OPERATOR_CANCELLED;
}
if (ss->totvert == 0) {
/* No geometry to trim or to detect a valid position for the trimming shape. */
if (!can_exec(*C)) {
return OPERATOR_CANCELLED;
}
@ -717,23 +731,11 @@ static int gesture_lasso_exec(bContext *C, wmOperator *op)
static int gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
const View3D *v3d = CTX_wm_view3d(C);
const Base *base = CTX_data_active_base(C);
if (!BKE_base_is_visible(v3d, base)) {
if (!can_invoke(*C)) {
return OPERATOR_CANCELLED;
}
SculptCursorGeometryInfo sgi;
const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
SCULPT_vertex_random_access_ensure(ss);
ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
if (ss->gesture_initial_hit) {
copy_v3_v3(ss->gesture_initial_location, sgi.location);
copy_v3_v3(ss->gesture_initial_normal, sgi.normal);
}
initialize_cursor_info(*C, event);
return WM_gesture_lasso_invoke(C, op, event);
}

View File

@ -596,7 +596,7 @@ static int file_select_exec(bContext *C, wmOperator *op)
int ret_val = OPERATOR_FINISHED;
const FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (sfile && params) {
if (params) {
int idx = params->highlight_file;
int numfiles = filelist_files_ensure(sfile->files);

View File

@ -174,7 +174,7 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L
* include the 'only selected' flag...
*/
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY |
ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIMFILTER_NODUPLIS);
/* FIXME: this should really be check for by the filtering code. */
if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) {
filter |= ANIMFILTER_SEL;

View File

@ -1477,6 +1477,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
nodeSetSelected(dst_node, true);
}
tree_draw_order_update(*snode->edittree);
ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}

View File

@ -1365,14 +1365,14 @@ void SEQUENCER_OT_image_strip_add(wmOperatorType *ot)
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
FILE_SPECIAL,
FILE_OPENFILE,
WM_FILESEL_DIRECTORY | WM_FILESEL_RELPATH | WM_FILESEL_FILES |
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
WM_operator_properties_filesel(
ot,
FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
FILE_SPECIAL,
FILE_OPENFILE,
(WM_FILESEL_DIRECTORY | WM_FILESEL_RELPATH | WM_FILESEL_FILES | WM_FILESEL_SHOW_PROPS),
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
sequencer_generic_props__internal(
ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD | SEQPROP_VIEW_TRANSFORM);

View File

@ -42,6 +42,8 @@
/* Own include. */
#include "transform_constraints.hh"
using namespace blender;
static void drawObjectConstraint(TransInfo *t);
/* -------------------------------------------------------------------- */
@ -381,6 +383,72 @@ static const float (*transform_object_axismtx_get(const TransInfo *t,
return td->axismtx;
}
void transform_constraint_get_nearest(const TransInfo *t, const float3 &vec, float r_vec[3])
{
bool is_snap_to_point = false, is_snap_to_edge = false, is_snap_to_face = false;
if (transform_snap_is_active(t)) {
if (validSnap(t)) {
is_snap_to_edge = (t->tsnap.target_type & SCE_SNAP_TO_EDGE) != 0;
is_snap_to_face = (t->tsnap.target_type & SCE_SNAP_TO_FACE) != 0;
is_snap_to_point = !is_snap_to_edge && !is_snap_to_face;
}
}
/* Fallback for when axes are aligned. */
mul_v3_m3v3(r_vec, t->con.pmtx, vec);
if (is_snap_to_point) {
/* Pass. With snap points, a projection is alright, no adjustments needed. */
}
else {
const int dims = getConstraintSpaceDimension(t);
if (dims == 2) {
if (!is_zero_v3(r_vec)) {
float plane_no[3];
constraint_plane_normal_calc(t, plane_no);
if (is_snap_to_edge) {
constraint_snap_plane_to_edge(t, plane_no, r_vec);
}
else if (is_snap_to_face) {
/* Disabled, as it has not proven to be really useful. (See #82386). */
// constraint_snap_plane_to_face(t, plane, out);
}
else if (!isPlaneProjectionViewAligned(t, plane_no)) {
/* View alignment correction. */
planeProjection(t, plane_no, vec, r_vec);
}
}
}
else if (dims == 1) {
float c[3];
if (t->con.mode & CON_AXIS0) {
copy_v3_v3(c, t->spacemtx[0]);
}
else if (t->con.mode & CON_AXIS1) {
copy_v3_v3(c, t->spacemtx[1]);
}
else {
BLI_assert(t->con.mode & CON_AXIS2);
copy_v3_v3(c, t->spacemtx[2]);
}
if (is_snap_to_edge) {
transform_constraint_snap_axis_to_edge(t, c, r_vec);
}
else if (is_snap_to_face) {
transform_constraint_snap_axis_to_face(t, c, r_vec);
}
else {
/* View alignment correction. */
axisProjection(t, c, vec, r_vec);
}
}
}
}
/**
* Generic callback for constant spatial constraints applied to linear motion
*
@ -394,71 +462,12 @@ static void applyAxisConstraintVec(const TransInfo *t,
const float in[3],
float out[3])
{
copy_v3_v3(out, in);
if (!td && t->con.mode & CON_APPLY) {
bool is_snap_to_point = false, is_snap_to_edge = false, is_snap_to_face = false;
if (transform_snap_is_active(t)) {
if (validSnap(t)) {
is_snap_to_edge = (t->tsnap.target_type & SCE_SNAP_TO_EDGE) != 0;
is_snap_to_face = (t->tsnap.target_type & SCE_SNAP_TO_FACE) != 0;
is_snap_to_point = !is_snap_to_edge && !is_snap_to_face;
}
}
/* Fallback for when axes are aligned. */
mul_m3_v3(t->con.pmtx, out);
if (is_snap_to_point) {
/* Pass. With snap points, a projection is alright, no adjustments needed. */
}
else {
const int dims = getConstraintSpaceDimension(t);
if (dims == 2) {
if (!is_zero_v3(out)) {
float plane_no[3];
constraint_plane_normal_calc(t, plane_no);
if (is_snap_to_edge) {
constraint_snap_plane_to_edge(t, plane_no, out);
}
else if (is_snap_to_face) {
/* Disabled, as it has not proven to be really useful. (See #82386). */
// constraint_snap_plane_to_face(t, plane, out);
}
else if (!isPlaneProjectionViewAligned(t, plane_no)) {
/* View alignment correction. */
planeProjection(t, plane_no, in, out);
}
}
}
else if (dims == 1) {
float c[3];
if (t->con.mode & CON_AXIS0) {
copy_v3_v3(c, t->spacemtx[0]);
}
else if (t->con.mode & CON_AXIS1) {
copy_v3_v3(c, t->spacemtx[1]);
}
else {
BLI_assert(t->con.mode & CON_AXIS2);
copy_v3_v3(c, t->spacemtx[2]);
}
if (is_snap_to_edge) {
transform_constraint_snap_axis_to_edge(t, c, out);
}
else if (is_snap_to_face) {
transform_constraint_snap_axis_to_face(t, c, out);
}
else {
/* View alignment correction. */
axisProjection(t, c, in, out);
}
}
}
if (td || !(t->con.mode & CON_APPLY)) {
copy_v3_v3(out, in);
return;
}
transform_constraint_get_nearest(t, in, out);
}
/**

View File

@ -23,6 +23,11 @@ void transform_constraint_snap_axis_to_edge(const TransInfo *t,
void transform_constraint_snap_axis_to_face(const TransInfo *t,
const float axis[3],
float r_out[3]);
void transform_constraint_get_nearest(const TransInfo *t,
const blender::float3 &vec,
float r_vec[3]);
void setConstraint(TransInfo *t, int mode, const char text[]);
/**
* Applies individual `td->axismtx` constraints.

View File

@ -43,6 +43,7 @@
#include "MEM_guardedalloc.h"
#include "transform.hh"
#include "transform_constraints.hh"
#include "transform_convert.hh"
#include "transform_mode.hh"
#include "transform_snap.hh"
@ -68,12 +69,8 @@ static void snap_source_center_fn(TransInfo *t);
static void snap_source_closest_fn(TransInfo *t);
static void snap_source_active_fn(TransInfo *t);
static eSnapMode snapObjectsTransform(TransInfo *t,
const float mval[2],
const float *vec,
float *dist_px,
float r_loc[3],
float r_no[3]);
static eSnapMode snapObjectsTransform(
TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]);
/** \} */
@ -1138,35 +1135,28 @@ static void snap_multipoints_free(TransInfo *t)
/** \name Calc Snap
* \{ */
static void snap_grid_uv_apply(TransInfo *t,
const float grid_dist[2],
const float vec[2],
float r_out[2])
static void snap_grid_uv_apply(TransInfo *t, const float grid_dist[2], float r_out[2])
{
const float *center_global = t->center_global;
float in[2];
float3 in;
convertViewVec(t, in, t->mval[0] - t->center2d[0], t->mval[1] - t->center2d[1]);
if (t->con.mode & CON_APPLY) {
/* We need to clear the previous Snap to Grid result,
* otherwise #t->con.applyVec will have no effect. */
t->tsnap.target_type = SCE_SNAP_TO_NONE;
t->con.applyVec(t, nullptr, nullptr, vec, in);
}
else {
copy_v2_v2(in, vec);
t->tsnap.status &= ~SNAP_TARGET_FOUND;
transform_constraint_get_nearest(t, in, in);
}
const float *center_global = t->center_global;
for (int i = 0; i < 2; i++) {
const float iter_fac = grid_dist[i];
r_out[i] = iter_fac * roundf((in[i] + center_global[i]) / iter_fac);
}
}
static bool snap_grid_uv(TransInfo *t, float vec[2], float r_val[2])
static bool snap_grid_uv(TransInfo *t, float r_val[2])
{
if (t->mode != TFM_TRANSLATION) {
return false;
}
float grid_dist[2];
mul_v2_v2v2(grid_dist, t->snap_spatial, t->aspect);
if (t->modifiers & MOD_PRECISION) {
@ -1178,7 +1168,7 @@ static bool snap_grid_uv(TransInfo *t, float vec[2], float r_val[2])
return false;
}
snap_grid_uv_apply(t, grid_dist, vec, r_val);
snap_grid_uv_apply(t, grid_dist, r_val);
t->tsnap.target_type = SCE_SNAP_TO_GRID;
return true;
}
@ -1189,7 +1179,7 @@ static bool snap_grid_uv(TransInfo *t, float vec[2], float r_val[2])
/** \name Calc Snap
* \{ */
static void snap_target_view3d_fn(TransInfo *t, float *vec)
static void snap_target_view3d_fn(TransInfo *t, float * /*vec*/)
{
BLI_assert(t->spacetype == SPACE_VIEW3D);
float loc[3];
@ -1200,7 +1190,7 @@ static void snap_target_view3d_fn(TransInfo *t, float *vec)
if (t->tsnap.mode & (SCE_SNAP_TO_GEOM | SCE_SNAP_TO_GRID)) {
zero_v3(no); /* objects won't set this */
snap_elem = snapObjectsTransform(t, t->mval, vec, &dist_px, loc, no);
snap_elem = snapObjectsTransform(t, t->mval, &dist_px, loc, no);
found = (snap_elem != SCE_SNAP_TO_NONE);
}
if ((found == false) && (t->tsnap.mode & SCE_SNAP_TO_VOLUME)) {
@ -1230,7 +1220,7 @@ static void snap_target_view3d_fn(TransInfo *t, float *vec)
t->tsnap.target_type = snap_elem;
}
static void snap_target_uv_fn(TransInfo *t, float *vec)
static void snap_target_uv_fn(TransInfo *t, float * /*vec*/)
{
BLI_assert(t->spacetype == SPACE_IMAGE);
if (t->tsnap.mode & (SCE_SNAP_TO_VERTEX | SCE_SNAP_TO_GRID)) {
@ -1252,7 +1242,7 @@ static void snap_target_uv_fn(TransInfo *t, float *vec)
t->tsnap.status |= SNAP_TARGET_FOUND;
}
else if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && snap_grid_uv(t, vec, t->tsnap.snap_target)) {
else if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && snap_grid_uv(t, t->tsnap.snap_target)) {
t->tsnap.status |= SNAP_TARGET_FOUND;
}
else {
@ -1432,7 +1422,7 @@ static void snap_source_closest_fn(TransInfo *t)
std::optional<blender::Bounds<blender::float3>> bounds;
if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
bounds = BKE_object_boundbox_get(td->ob);
bounds = BKE_object_boundbox_eval_cached_get(td->ob);
}
/* Use bound-box if possible. */
@ -1518,12 +1508,8 @@ static void snap_source_closest_fn(TransInfo *t)
/** \name Snap Objects
* \{ */
static eSnapMode snapObjectsTransform(TransInfo *t,
const float mval[2],
const float *vec,
float *dist_px,
float r_loc[3],
float r_no[3])
static eSnapMode snapObjectsTransform(
TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
{
SnapObjectParams snap_object_params{};
snap_object_params.snap_target_select = t->tsnap.target_operation;
@ -1533,13 +1519,12 @@ static eSnapMode snapObjectsTransform(TransInfo *t,
float *prev_co = (t->tsnap.status & SNAP_SOURCE_FOUND) ? t->tsnap.snap_source : t->center_global;
float *grid_co = nullptr, grid_co_stack[3];
if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && (t->con.mode & CON_APPLY) &&
(t->mode == TFM_TRANSLATION))
{
if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && (t->con.mode & CON_APPLY)) {
/* Without this position adjustment, the snap may be far from the expected constraint point. */
grid_co = grid_co_stack;
convertViewVec(t, grid_co, mval[0] - t->center2d[0], mval[1] - t->center2d[1]);
t->tsnap.status &= ~SNAP_TARGET_FOUND;
t->con.applyVec(t, nullptr, nullptr, vec, grid_co);
transform_constraint_get_nearest(t, grid_co, grid_co);
add_v3_v3(grid_co, t->center_global);
}

View File

@ -58,7 +58,7 @@ enum eGPUBarrier {
// GPU_BARRIER_CLIENT_MAPPED_BUFFER = (1 << 15), /* Not implemented yet. */
};
ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_UNIFORM)
ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_BUFFER_UPDATE)
/* NOTE: For Metal and Vulkan only.
* TODO(Metal): Update barrier calls to use stage flags. */

View File

@ -24,6 +24,24 @@ float compatible_fmod(float a, float b)
return 0.0;
}
vec2 compatible_fmod(vec2 a, float b)
{
return vec2(compatible_fmod(a.x, b), compatible_fmod(a.y, b));
}
vec3 compatible_fmod(vec3 a, float b)
{
return vec3(compatible_fmod(a.x, b), compatible_fmod(a.y, b), compatible_fmod(a.z, b));
}
vec4 compatible_fmod(vec4 a, float b)
{
return vec4(compatible_fmod(a.x, b),
compatible_fmod(a.y, b),
compatible_fmod(a.z, b),
compatible_fmod(a.w, b));
}
float compatible_pow(float x, float y)
{
if (y == 0.0) { /* x^0 -> 1, including 0^0 */

View File

@ -2,6 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
/* clang-format off */
@ -260,8 +261,11 @@ float noise_scale4(float result)
float snoise(float p)
{
float r = noise_perlin(p);
return (isinf(r)) ? 0.0 : noise_scale1(r);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
p = compatible_fmod(p, 100000.0);
return noise_scale1(noise_perlin(p));
}
float noise(float p)
@ -271,8 +275,12 @@ float noise(float p)
float snoise(vec2 p)
{
float r = noise_perlin(p);
return (isinf(r)) ? 0.0 : noise_scale2(r);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
p = compatible_fmod(p, 100000.0);
return noise_scale2(noise_perlin(p));
}
float noise(vec2 p)
@ -282,8 +290,12 @@ float noise(vec2 p)
float snoise(vec3 p)
{
float r = noise_perlin(p);
return (isinf(r)) ? 0.0 : noise_scale3(r);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
p = compatible_fmod(p, 100000.0);
return noise_scale3(noise_perlin(p));
}
float noise(vec3 p)
@ -293,8 +305,12 @@ float noise(vec3 p)
float snoise(vec4 p)
{
float r = noise_perlin(p);
return (isinf(r)) ? 0.0 : noise_scale4(r);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
p = compatible_fmod(p, 100000.0);
return noise_scale4(noise_perlin(p));
}
float noise(vec4 p)

View File

@ -429,7 +429,7 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream,
}
if (image->comps[i].sgnd) {
signed_offsets[i] = 1 << (image->comps[i].prec - 1);
signed_offsets[i] = long(1) << (image->comps[i].prec - 1);
}
/* only needed for float images but doesn't hurt to calc this */

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