WIP: Mesh: Improve and simplify modifier evaluation logic #119968
|
@ -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__":
|
||||
|
|
|
@ -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]:
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)}")
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -10,6 +10,7 @@ set(INC
|
|||
../../makesrna
|
||||
../../sequencer
|
||||
../../windowmanager
|
||||
../../../../extern/fmtlib/include
|
||||
# RNA_prototypes.h
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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", "");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue