WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 357 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
221 changed files with 9873 additions and 1241 deletions
Showing only changes of commit 9e2f88acfe - Show all commits

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,6 +32,7 @@ endif()
add_subdirectory(rangetree)
add_subdirectory(nanosvg)
add_subdirectory(wcwidth)
add_subdirectory(xxhash)
if(WITH_BULLET)
if(NOT WITH_SYSTEM_BULLET)

View File

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

21
extern/xxhash/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,21 @@
# SPDX-FileCopyrightText: 2024 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
set(INC
PUBLIC .
)
set(INC_SYS
)
set(SRC
xxhash.c
xxhash.h
)
set(LIB
)
blender_add_lib(extern_xxhash "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
add_library(bf::extern::xxhash ALIAS extern_xxhash)

26
extern/xxhash/LICENSE vendored Normal file
View File

@ -0,0 +1,26 @@
xxHash Library
Copyright (c) 2012-2021 Yann Collet
All rights reserved.
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

6
extern/xxhash/README.blender vendored Normal file
View File

@ -0,0 +1,6 @@
Project: xxHash
URL: https://xxhash.com/
License: BSD 2-Clause
Upstream version: v0.8.2 (2023-07-21)
Local modifications:
* None

43
extern/xxhash/xxhash.c vendored Normal file
View File

@ -0,0 +1,43 @@
/*
* xxHash - Extremely Fast Hash algorithm
* Copyright (C) 2012-2021 Yann Collet
*
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at:
* - xxHash homepage: https://www.xxhash.com
* - xxHash source repository: https://github.com/Cyan4973/xxHash
*/
/*
* xxhash.c instantiates functions defined in xxhash.h
*/
#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
#define XXH_IMPLEMENTATION /* access definitions */
#include "xxhash.h"

6773
extern/xxhash/xxhash.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,23 @@
The 2-Clause BSD License
------------------------
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

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

View File

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

View File

@ -73,7 +73,8 @@ bool visualkey_can_use(PointerRNA *ptr, PropertyRNA *prop)
has_parent = (pchan->parent != nullptr);
}
else {
BLI_assert(!"visualkey_can_use called for data-block that is not an Object or PoseBone.");
BLI_assert_msg(false,
"visualkey_can_use called for data-block that is not an Object or PoseBone.");
return false;
}

View File

@ -280,7 +280,7 @@ std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogDefinitionFile::copy_and
continue;
}
BLI_assert(!"A CDF should only reference known catalogs.");
BLI_assert_msg(false, "A CDF should only reference known catalogs.");
}
return copy;

View File

@ -89,23 +89,11 @@ class BlobWriteSharing : NonCopyable, NonMovable {
*/
Map<const ImplicitSharingInfo *, StoredByRuntimeValue> stored_by_runtime_;
struct SliceHash {
uint64_t a;
uint64_t b;
BLI_STRUCT_EQUALITY_OPERATORS_2(SliceHash, a, b)
uint64_t hash() const
{
return get_default_hash(this->a, this->b);
}
};
/**
* Remembers where data was stored based on the hash of the data. This allows us to skip writing
* the same array again if it has the same hash.
*/
Map<SliceHash, BlobSlice> slice_by_content_hash_;
Map<uint64_t, BlobSlice> slice_by_content_hash_;
public:
~BlobWriteSharing();

View File

@ -11,7 +11,7 @@
#include "BLI_bounds_types.hh"
#include "BLI_generic_virtual_array.hh"
#include "BLI_implicit_sharing.hh"
#include "BLI_implicit_sharing_ptr.hh"
#include "BLI_index_mask_fwd.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
@ -451,7 +451,7 @@ class CurvesEditHints {
* Evaluated positions for the points in #curves_orig. If this is empty, the positions from the
* evaluated #Curves should be used if possible.
*/
std::optional<Array<float3>> positions;
ImplicitSharingPtrAndData positions_data;
/**
* Matrices which transform point movement vectors from original data to corresponding movements
* of evaluated data.
@ -460,6 +460,9 @@ class CurvesEditHints {
CurvesEditHints(const Curves &curves_id_orig) : curves_id_orig(curves_id_orig) {}
std::optional<Span<float3>> positions() const;
std::optional<MutableSpan<float3>> positions_for_write();
/**
* The edit hints have to correspond to the original curves, i.e. the number of deformed points
* is the same as the number of original points.

View File

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

View File

@ -17,22 +17,38 @@ struct BMEditMesh;
namespace blender::bke {
struct EditMeshData {
/** when set, \a vertexNos, faceNos are lazy initialized */
/**
* Deformed positions calculated by modifiers in the modifier stack that can can process an
* edit mesh input. When this is not empty, the other arrays will depend on the values.
*/
Array<float3> vert_positions;
/** lazy initialize (when \a vert_positions is set) */
/**
* Lazily initialized vertex normal cache (used when `vert_positions` is set.
* Access via #BKE_editmesh_cache_ensure_vert_normals instead of directly.
*/
Array<float3> vert_normals;
/**
* Lazily initialized face normal cache (used when `vert_positions` is set.
* Access via #BKE_editmesh_cache_ensure_face_normals instead of directly.
*/
Array<float3> face_normals;
/** also lazy init but don't depend on \a vert_positions */
/**
* Cache of face centers, also depends on `vert_positions` when it is not empty.
* Access via #BKE_editmesh_cache_ensure_face_centers instead of directly.
*/
Array<float3> face_centers;
};
} // namespace blender::bke
void BKE_editmesh_cache_ensure_face_normals(BMEditMesh &em, blender::bke::EditMeshData &emd);
void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh &em, blender::bke::EditMeshData &emd);
blender::Span<blender::float3> BKE_editmesh_cache_ensure_face_normals(
BMEditMesh &em, blender::bke::EditMeshData &emd);
blender::Span<blender::float3> BKE_editmesh_cache_ensure_vert_normals(
BMEditMesh &em, blender::bke::EditMeshData &emd);
void BKE_editmesh_cache_ensure_face_centers(BMEditMesh &em, blender::bke::EditMeshData &emd);
blender::Span<blender::float3> BKE_editmesh_cache_ensure_face_centers(
BMEditMesh &em, blender::bke::EditMeshData &emd);
std::optional<blender::Bounds<blender::float3>> BKE_editmesh_cache_calc_minmax(
const BMEditMesh &em, const blender::bke::EditMeshData &emd);

View File

@ -14,6 +14,7 @@
#include "BLI_array_utils.hh"
#include "BLI_color.hh"
#include "BLI_function_ref.hh"
#include "BLI_implicit_sharing_ptr.hh"
#include "BLI_map.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
@ -76,12 +77,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
*/
@ -773,7 +774,11 @@ class GreasePencilRuntime {
class GreasePencilDrawingEditHints {
public:
std::optional<Array<float3>> positions;
const greasepencil::Drawing *drawing_orig;
ImplicitSharingPtrAndData positions_data;
std::optional<Span<float3>> positions() const;
std::optional<MutableSpan<float3>> positions_for_write();
};
/**

View File

@ -83,7 +83,7 @@ bool BKE_linestyle_geometry_modifier_move(FreestyleLineStyle *linestyle,
void BKE_linestyle_modifier_list_color_ramps(FreestyleLineStyle *linestyle, ListBase *listbase);
std::optional<std::string> BKE_linestyle_path_to_color_ramp(FreestyleLineStyle *linestyle,
struct ColorBand *color_ramp);
const struct ColorBand *color_ramp);
bool BKE_linestyle_use_textures(FreestyleLineStyle *linestyle, bool use_shading_nodes);

View File

@ -67,8 +67,8 @@ void BKE_mask_point_free(struct MaskSplinePoint *point);
void BKE_mask_layer_unique_name(struct Mask *mask, struct MaskLayer *masklay);
void BKE_mask_layer_rename(struct Mask *mask,
struct MaskLayer *masklay,
char *oldname,
char *newname);
const char *oldname,
const char *newname);
struct MaskLayer *BKE_mask_layer_copy(const struct MaskLayer *masklay);
void BKE_mask_layer_copy_list(struct ListBase *masklayers_new, const struct ListBase *masklayers);

View File

@ -494,9 +494,6 @@ bool BKE_mesh_validate_all_customdata(CustomData *vert_data,
void BKE_mesh_strip_loose_faces(Mesh *mesh);
/* In DerivedMesh.cc */
void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *mesh_eval);
/* **** Depsgraph evaluation **** */
void BKE_mesh_eval_geometry(Depsgraph *depsgraph, Mesh *mesh);

View File

@ -163,11 +163,6 @@ struct MeshRuntime {
/** #eMeshWrapperType and others. */
eMeshWrapperType wrapper_type = ME_WRAPPER_TYPE_MDATA;
/**
* A type mask from wrapper_type,
* in case there are differences in finalizing logic between types.
*/
eMeshWrapperType wrapper_type_finalize = ME_WRAPPER_TYPE_MDATA;
/**
* Settings for lazily evaluating the subdivision on the CPU if needed. These are

View File

@ -87,7 +87,7 @@ void BKE_movieclip_build_proxy_frame(struct MovieClip *clip,
int clip_flag,
struct MovieDistortion *distortion,
int cfra,
int *build_sizes,
const int *build_sizes,
int build_count,
bool undistorted);
@ -99,7 +99,7 @@ void BKE_movieclip_build_proxy_frame_for_ibuf(struct MovieClip *clip,
struct ImBuf *ibuf,
struct MovieDistortion *distortion,
int cfra,
int *build_sizes,
const int *build_sizes,
int build_count,
bool undistorted);
bool BKE_movieclip_proxy_enabled(struct MovieClip *clip);

View File

@ -123,7 +123,7 @@ int BKE_packedfile_count_all(struct Main *bmain);
*/
enum ePF_FileCompare BKE_packedfile_compare_to_file(const char *ref_file_name,
const char *filepath_rel,
struct PackedFile *pf);
const struct PackedFile *pf);
/* Read. */
@ -143,7 +143,7 @@ void BKE_packedfile_id_unpack(struct Main *bmain,
struct ReportList *reports,
enum ePF_FileStatus how);
void BKE_packedfile_blend_write(struct BlendWriter *writer, struct PackedFile *pf);
void BKE_packedfile_blend_write(struct BlendWriter *writer, const struct PackedFile *pf);
void BKE_packedfile_blend_read(struct BlendDataReader *reader, struct PackedFile **pf_p);
#ifdef __cplusplus

View File

@ -220,11 +220,8 @@ void BKE_subdiv_ccg_topology_counters(const SubdivCCG &subdiv_ccg,
int &r_num_loops);
struct SubdivCCGNeighbors {
SubdivCCGCoord *coords;
int size;
blender::Array<SubdivCCGCoord, 256> coords;
int num_duplicates;
SubdivCCGCoord coords_fixed[256];
};
void BKE_subdiv_ccg_print_coord(const char *message, const SubdivCCGCoord &coord);
@ -244,9 +241,6 @@ bool BKE_subdiv_ccg_check_coord_valid(const SubdivCCG &subdiv_ccg, const SubdivC
* element inside of every neighboring grid. */
/* Get actual neighbors of the given coordinate.
*
* SubdivCCGNeighbors.neighbors must be freed if it is not equal to
* SubdivCCGNeighbors.fixed_neighbors.
*
* If include_duplicates is true, vertices in other grids that match
* the current vertex are added at the end of the coords array. */

View File

@ -65,7 +65,7 @@ void txt_clean_text(struct Text *text);
void txt_order_cursors(struct Text *text, bool reverse);
int txt_find_string(struct Text *text, const char *findstr, int wrap, int match_case);
bool txt_has_sel(const struct Text *text);
int txt_get_span(struct TextLine *from, const struct TextLine *to);
int txt_get_span(const struct TextLine *from, const struct TextLine *to);
void txt_move_up(struct Text *text, bool sel);
void txt_move_down(struct Text *text, bool sel);
void txt_move_left(struct Text *text, bool sel);

View File

@ -580,6 +580,7 @@ set(LIB
bf_shader_fx
bf_simulation
PRIVATE bf::extern::fmtlib
PRIVATE bf::extern::xxhash
PRIVATE bf::intern::atomic
# For `vfontdata_freetype.c`.
${FREETYPE_LIBRARIES} ${BROTLI_LIBRARIES}

View File

@ -221,7 +221,8 @@ struct CCGSubSurf {
(void)0
#define NormCopy(av, bv) \
{ \
float *_a = (float *)av, *_b = (float *)bv; \
float *_a = (float *)av; \
const float *_b = (const float *)bv; \
_a[0] = _b[0]; \
_a[1] = _b[1]; \
_a[2] = _b[2]; \
@ -229,7 +230,8 @@ struct CCGSubSurf {
(void)0
#define NormAdd(av, bv) \
{ \
float *_a = (float *)av, *_b = (float *)bv; \
float *_a = (float *)av; \
const float *_b = (const float *)bv; \
_a[0] += _b[0]; \
_a[1] += _b[1]; \
_a[2] += _b[2]; \

View File

@ -70,12 +70,16 @@ static float *_face_getIFNoEdge(CCGFace *f,
static void _face_calcIFNo(
CCGFace *f, int lvl, int S, int x, int y, float no[3], int levels, int dataSize)
{
float *a = static_cast<float *>(ccg_face_getIFCo(f, lvl, S, x + 0, y + 0, levels, dataSize));
float *b = static_cast<float *>(ccg_face_getIFCo(f, lvl, S, x + 1, y + 0, levels, dataSize));
float *c = static_cast<float *>(ccg_face_getIFCo(f, lvl, S, x + 1, y + 1, levels, dataSize));
float *d = static_cast<float *>(ccg_face_getIFCo(f, lvl, S, x + 0, y + 1, levels, dataSize));
float a_cX = c[0] - a[0], a_cY = c[1] - a[1], a_cZ = c[2] - a[2];
float b_dX = d[0] - b[0], b_dY = d[1] - b[1], b_dZ = d[2] - b[2];
const float *a = static_cast<float *>(
ccg_face_getIFCo(f, lvl, S, x + 0, y + 0, levels, dataSize));
const float *b = static_cast<float *>(
ccg_face_getIFCo(f, lvl, S, x + 1, y + 0, levels, dataSize));
const float *c = static_cast<float *>(
ccg_face_getIFCo(f, lvl, S, x + 1, y + 1, levels, dataSize));
const float *d = static_cast<float *>(
ccg_face_getIFCo(f, lvl, S, x + 0, y + 1, levels, dataSize));
const float a_cX = c[0] - a[0], a_cY = c[1] - a[1], a_cZ = c[2] - a[2];
const float b_dX = d[0] - b[0], b_dY = d[1] - b[1], b_dZ = d[2] - b[2];
no[0] = b_dY * a_cZ - b_dZ * a_cY;
no[1] = b_dZ * a_cX - b_dX * a_cZ;

View File

@ -89,8 +89,6 @@ using blender::bke::MeshComponent;
#endif
static void mesh_init_origspace(Mesh *mesh);
static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final);
static void editbmesh_calc_modifier_final_normals_or_defer(Mesh *mesh_final);
/* -------------------------------------------------------------------- */
@ -465,16 +463,6 @@ static void mesh_calc_finalize(const Mesh *mesh_input, Mesh *mesh_eval)
mesh_eval->runtime->edit_mesh = mesh_input->runtime->edit_mesh;
}
void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *mesh_eval)
{
if (mesh_eval->runtime->wrapper_type_finalize & (1 << ME_WRAPPER_TYPE_BMESH)) {
editbmesh_calc_modifier_final_normals(mesh_eval);
mesh_eval->runtime->wrapper_type_finalize = eMeshWrapperType(
mesh_eval->runtime->wrapper_type_finalize & ~(1 << ME_WRAPPER_TYPE_BMESH));
}
BLI_assert(mesh_eval->runtime->wrapper_type_finalize == 0);
}
/**
* Modifies the given mesh and geometry set. The mesh is not passed as part of the mesh component
* in the \a geometry_set input, it is only passed in \a input_mesh and returned in the return
@ -1001,36 +989,6 @@ bool editbmesh_modifier_is_enabled(const Scene *scene,
return true;
}
static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final)
{
switch (mesh_final->runtime->wrapper_type) {
case ME_WRAPPER_TYPE_SUBD:
case ME_WRAPPER_TYPE_MDATA:
break;
case ME_WRAPPER_TYPE_BMESH: {
BMEditMesh &em = *mesh_final->runtime->edit_mesh;
blender::bke::EditMeshData &emd = *mesh_final->runtime->edit_data;
if (!emd.vert_positions.is_empty()) {
BKE_editmesh_cache_ensure_vert_normals(em, emd);
BKE_editmesh_cache_ensure_face_normals(em, emd);
}
return;
}
}
}
static void editbmesh_calc_modifier_final_normals_or_defer(Mesh *mesh_final)
{
if (mesh_final->runtime->wrapper_type != ME_WRAPPER_TYPE_MDATA) {
/* Generated at draw time. */
mesh_final->runtime->wrapper_type_finalize = eMeshWrapperType(
1 << mesh_final->runtime->wrapper_type);
return;
}
editbmesh_calc_modifier_final_normals(mesh_final);
}
static MutableSpan<float3> mesh_wrapper_vert_coords_ensure_for_write(Mesh *mesh)
{
switch (mesh->runtime->wrapper_type) {
@ -1244,12 +1202,6 @@ static void editbmesh_calc_modifiers(Depsgraph *depsgraph,
BKE_id_free(nullptr, mesh_orco);
}
/* Compute normals. */
editbmesh_calc_modifier_final_normals_or_defer(mesh_final);
if (mesh_cage && (mesh_cage != mesh_final)) {
editbmesh_calc_modifier_final_normals_or_defer(mesh_cage);
}
/* Return final mesh. */
*r_final = mesh_final;
if (r_cage) {

View File

@ -350,8 +350,8 @@ void action_group_colors_sync(bActionGroup *grp, const bActionGroup *ref_grp)
if (grp->customCol) {
if (grp->customCol > 0) {
/* copy theme colors on-to group's custom color in case user tries to edit color */
bTheme *btheme = static_cast<bTheme *>(U.themes.first);
ThemeWireColor *col_set = &btheme->tarm[(grp->customCol - 1)];
const bTheme *btheme = static_cast<const bTheme *>(U.themes.first);
const ThemeWireColor *col_set = &btheme->tarm[(grp->customCol - 1)];
memcpy(&grp->cs, col_set, sizeof(ThemeWireColor));
}

View File

@ -2850,8 +2850,9 @@ static void nlastrip_evaluate_transition(const int evaluation_mode,
break;
}
case STRIP_EVAL_NOBLEND: {
BLI_assert( !"This case shouldn't occur. Transitions assumed to not reference other "
"transitions. ");
BLI_assert_msg(false,
"This case shouldn't occur. "
"Transitions assumed to not reference other transitions.");
break;
}
}

View File

@ -14,7 +14,6 @@
#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_hash_md5.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_path_util.h"
@ -26,6 +25,7 @@
#include <fmt/format.h>
#include <sstream>
#include <xxhash.h>
#ifdef WITH_OPENVDB
# include <openvdb/io/Stream.h>
@ -192,8 +192,7 @@ DictionaryValuePtr BlobWriteSharing::write_implicitly_shared(
std::shared_ptr<io::serialize::DictionaryValue> BlobWriteSharing::write_deduplicated(
BlobWriter &writer, const void *data, const int64_t size_in_bytes)
{
SliceHash content_hash;
BLI_hash_md5_buffer(static_cast<const char *>(data), size_in_bytes, &content_hash);
const uint64_t content_hash = XXH3_64bits(data, size_in_bytes);
const BlobSlice slice = slice_by_content_hash_.lookup_or_add_cb(
content_hash, [&]() { return writer.write(data, size_in_bytes); });
return slice.serialize();

View File

@ -182,7 +182,7 @@ static BlendHandle *link_append_context_library_blohandle_ensure(
lib_context->bf_reports.reports = reports;
}
char *libname = lib_context->path;
const char *libname = lib_context->path;
BlendHandle *blo_handle = lib_context->blo_handle;
if (blo_handle == nullptr) {
if (STREQ(libname, BLO_EMBEDDED_STARTUP_BLEND)) {
@ -1410,7 +1410,7 @@ void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *re
{
BlendfileLinkAppendContextLibrary *lib_context =
static_cast<BlendfileLinkAppendContextLibrary *>(liblink->link);
char *libname = lib_context->path;
const char *libname = lib_context->path;
BlendHandle *blo_handle = link_append_context_library_blohandle_ensure(
lapp_context, lib_context, reports);

View File

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

View File

@ -599,9 +599,9 @@ GeometryDeformation get_evaluated_curves_deformation(const Object *ob_eval, cons
if (edit_component_eval != nullptr) {
const CurvesEditHints *edit_hints = edit_component_eval->curves_edit_hints_.get();
if (edit_hints != nullptr && &edit_hints->curves_id_orig == &curves_id_orig) {
if (edit_hints->positions.has_value()) {
BLI_assert(edit_hints->positions->size() == points_num);
deformation.positions = *edit_hints->positions;
if (const std::optional<Span<float3>> positions = edit_hints->positions()) {
BLI_assert(positions->size() == points_num);
deformation.positions = *positions;
uses_extra_positions = true;
}
if (edit_hints->deform_mats.has_value()) {
@ -677,8 +677,8 @@ GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object
BLI_assert(edit_hints->drawing_hints->size() == layers_orig.size());
const GreasePencilDrawingEditHints &drawing_hints =
edit_hints->drawing_hints.value()[layer_index];
if (drawing_hints.positions.has_value()) {
deformation.positions = *drawing_hints.positions;
if (const std::optional<Span<float3>> positions = drawing_hints.positions()) {
deformation.positions = *positions;
return deformation;
}
}

View File

@ -4750,7 +4750,6 @@ bool BKE_nurb_valid_message(const int pnts,
NURBSValidationStatus status = nurb_check_valid(
pnts, order, flag, type, is_surf, &points_needed);
const char *msg_template = nullptr;
switch (status) {
case NURBSValidationStatus::Valid:
message_dst[0] = 0;
@ -4761,20 +4760,24 @@ bool BKE_nurb_valid_message(const int pnts,
message_dst[0] = 0;
return false;
}
msg_template = RPT_("At least two points required");
BLI_strncpy(message_dst, RPT_("At least two points required"), maxncpy);
break;
case NURBSValidationStatus::MorePointsThanOrderRequired:
msg_template = RPT_("Must have more control points than Order");
BLI_strncpy(message_dst, RPT_("Must have more control points than Order"), maxncpy);
break;
case NURBSValidationStatus::MoreRowsForBezierRequired:
msg_template = RPT_("%d more %s row(s) needed for Bézier");
BLI_snprintf(message_dst,
maxncpy,
RPT_("%d more %s row(s) needed for Bézier"),
points_needed,
dir == 0 ? "U" : "V");
break;
case NURBSValidationStatus::MorePointsForBezierRequired:
msg_template = RPT_("%d more point(s) needed for Bézier");
BLI_snprintf(
message_dst, maxncpy, RPT_("%d more point(s) needed for Bézier"), points_needed);
break;
}
BLI_snprintf(message_dst, maxncpy, msg_template, points_needed, dir == 0 ? "U" : "V");
return true;
}

View File

@ -333,8 +333,8 @@ CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const
bool CurvesEditHints::is_valid() const
{
const int point_num = this->curves_id_orig.geometry.point_num;
if (this->positions.has_value()) {
if (this->positions->size() != point_num) {
if (this->positions().has_value()) {
if (this->positions()->size() != point_num) {
return false;
}
}
@ -346,6 +346,35 @@ bool CurvesEditHints::is_valid() const
return true;
}
std::optional<Span<float3>> CurvesEditHints::positions() const
{
if (!this->positions_data.has_value()) {
return std::nullopt;
}
const int points_num = this->curves_id_orig.geometry.wrap().points_num();
return Span(static_cast<const float3 *>(this->positions_data.data), points_num);
}
std::optional<MutableSpan<float3>> CurvesEditHints::positions_for_write()
{
if (!this->positions_data.has_value()) {
return std::nullopt;
}
const int points_num = this->curves_id_orig.geometry.wrap().points_num();
ImplicitSharingPtrAndData &data = this->positions_data;
if (data.sharing_info->is_mutable()) {
data.sharing_info->tag_ensured_mutable();
}
else {
auto *new_sharing_info = new ImplicitSharedValue<Array<float3>>(*this->positions());
data.sharing_info = ImplicitSharingPtr<ImplicitSharingInfo>(new_sharing_info);
data.data = new_sharing_info->data.data();
}
return MutableSpan(const_cast<float3 *>(static_cast<const float3 *>(data.data)), points_num);
}
void curves_normals_point_domain_calc(const CurvesGeometry &curves, MutableSpan<float3> normals)
{
const bke::CurvesFieldContext context(curves, AttrDomain::Point);

View File

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

View File

@ -17,14 +17,18 @@
#include "BKE_editmesh.hh"
#include "BKE_editmesh_cache.hh" /* own include */
using blender::float3;
using blender::Span;
/* -------------------------------------------------------------------- */
/** \name Ensure Data (derived from coords)
* \{ */
void BKE_editmesh_cache_ensure_face_normals(BMEditMesh &em, blender::bke::EditMeshData &emd)
Span<float3> BKE_editmesh_cache_ensure_face_normals(BMEditMesh &em,
blender::bke::EditMeshData &emd)
{
if (emd.vert_positions.is_empty() || !emd.face_normals.is_empty()) {
return;
return emd.face_normals;
}
BMesh *bm = em.bm;
@ -36,37 +40,35 @@ void BKE_editmesh_cache_ensure_face_normals(BMEditMesh &em, blender::bke::EditMe
int i;
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
BM_elem_index_set(efa, i); /* set_inline */
BM_face_calc_normal_vcos(bm,
efa,
emd.face_normals[i],
reinterpret_cast<const float(*)[3]>(emd.vert_positions.data()));
BM_face_calc_normal_vcos(bm, efa, emd.face_normals[i], emd.vert_positions);
}
bm->elem_index_dirty &= ~BM_FACE;
return emd.face_normals;
}
void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh &em, blender::bke::EditMeshData &emd)
Span<float3> BKE_editmesh_cache_ensure_vert_normals(BMEditMesh &em,
blender::bke::EditMeshData &emd)
{
if (emd.vert_positions.is_empty() || !emd.vert_normals.is_empty()) {
return;
return emd.vert_normals;
}
BMesh *bm = em.bm;
/* Calculate vertex normals from face normals. */
BKE_editmesh_cache_ensure_face_normals(em, emd);
const Span<float3> face_normals = BKE_editmesh_cache_ensure_face_normals(em, emd);
emd.vert_normals.reinitialize(bm->totvert);
BM_mesh_elem_index_ensure(bm, BM_FACE);
BM_verts_calc_normal_vcos(bm,
reinterpret_cast<const float(*)[3]>(emd.face_normals.data()),
reinterpret_cast<const float(*)[3]>(emd.vert_positions.data()),
reinterpret_cast<float(*)[3]>(emd.vert_normals.data()));
BM_verts_calc_normal_vcos(bm, face_normals, emd.vert_positions, emd.vert_normals);
return emd.vert_normals;
}
void BKE_editmesh_cache_ensure_face_centers(BMEditMesh &em, blender::bke::EditMeshData &emd)
Span<float3> BKE_editmesh_cache_ensure_face_centers(BMEditMesh &em,
blender::bke::EditMeshData &emd)
{
if (!emd.face_centers.is_empty()) {
return;
return emd.face_centers;
}
BMesh *bm = em.bm;
@ -86,6 +88,7 @@ void BKE_editmesh_cache_ensure_face_centers(BMEditMesh &em, blender::bke::EditMe
BM_face_calc_center_median_vcos(bm, efa, emd.face_centers[i], emd.vert_positions);
}
}
return emd.face_centers;
}
/** \} */

View File

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

View File

@ -40,25 +40,37 @@ void GeometryComponentEditData::clear()
grease_pencil_edit_hints_.reset();
}
static ImplicitSharingPtrAndData save_shared_attribute(const GAttributeReader &attribute)
{
if (attribute.sharing_info && attribute.varray.is_span()) {
const void *data = attribute.varray.get_internal_span().data();
attribute.sharing_info->add_user();
return {ImplicitSharingPtr(attribute.sharing_info), data};
}
auto *data = new ImplicitSharedValue<GArray<>>(attribute.varray.type(), attribute.varray.size());
attribute.varray.materialize(data->data.data());
return {ImplicitSharingPtr<ImplicitSharingInfo>(data), data->data.data()};
}
static void remember_deformed_curve_positions_if_necessary(
const Curves *curves_id, GeometryComponentEditData &edit_component)
{
if (!edit_component.curves_edit_hints_) {
return;
}
if (edit_component.curves_edit_hints_->positions.has_value()) {
if (curves_id == nullptr) {
return;
}
if (curves_id == nullptr) {
CurvesEditHints &edit_hints = *edit_component.curves_edit_hints_;
if (edit_hints.positions().has_value()) {
return;
}
const CurvesGeometry &curves = curves_id->geometry.wrap();
const int points_num = curves.points_num();
if (points_num != edit_component.curves_edit_hints_->curves_id_orig.geometry.point_num) {
if (points_num != edit_hints.curves_id_orig.geometry.point_num) {
return;
}
edit_component.curves_edit_hints_->positions.emplace(points_num);
edit_component.curves_edit_hints_->positions->as_mutable_span().copy_from(curves.positions());
edit_hints.positions_data = save_shared_attribute(curves.attributes().lookup("position"));
}
static void remember_deformed_grease_pencil_if_necessary(const GreasePencil *grease_pencil,
@ -91,14 +103,15 @@ static void remember_deformed_grease_pencil_if_necessary(const GreasePencil *gre
const greasepencil::Drawing *orig_drawing = orig_grease_pencil.get_drawing_at(
orig_layer, grease_pencil->runtime->eval_frame);
GreasePencilDrawingEditHints &drawing_hints = all_hints[layer_index];
if (!drawing || !orig_drawing) {
continue;
}
if (drawing->strokes().points_num() != orig_drawing->strokes().points_num()) {
drawing_hints.drawing_orig = orig_drawing;
const CurvesGeometry &curves = drawing->strokes();
if (curves.points_num() != orig_drawing->strokes().points_num()) {
continue;
}
drawing_hints.positions.emplace(drawing->strokes().positions());
drawing_hints.positions_data = save_shared_attribute(curves.attributes().lookup("position"));
}
}

View File

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

View File

@ -578,7 +578,7 @@ void Drawing::set_texture_matrices(Span<float4x2> matrices, const IndexMask &sel
const float4x2 texspace = matrices[pos];
/* We do the computation using doubles to avoid numerical precision errors. */
double4x3 strokemat4x3 = double4x3(expand_4x2_mat(strokemat));
const double4x3 strokemat4x3 = double4x3(expand_4x2_mat(strokemat));
/*
* We want to solve for `texture_matrix` in the equation: `texspace = texture_matrix *
@ -1570,6 +1570,40 @@ void LayerGroup::update_from_dna_read()
} // namespace blender::bke::greasepencil
namespace blender::bke {
std::optional<Span<float3>> GreasePencilDrawingEditHints::positions() const
{
if (!this->positions_data.has_value()) {
return std::nullopt;
}
const int points_num = this->drawing_orig->geometry.wrap().points_num();
return Span(static_cast<const float3 *>(this->positions_data.data), points_num);
}
std::optional<MutableSpan<float3>> GreasePencilDrawingEditHints::positions_for_write()
{
if (!this->positions_data.has_value()) {
return std::nullopt;
}
const int points_num = this->drawing_orig->geometry.wrap().points_num();
ImplicitSharingPtrAndData &data = this->positions_data;
if (data.sharing_info->is_mutable()) {
/* If the referenced component is already mutable, return it directly. */
data.sharing_info->tag_ensured_mutable();
}
else {
auto *new_sharing_info = new ImplicitSharedValue<Array<float3>>(*this->positions());
data.sharing_info = ImplicitSharingPtr<ImplicitSharingInfo>(new_sharing_info);
data.data = new_sharing_info->data.data();
}
return MutableSpan(const_cast<float3 *>(static_cast<const float3 *>(data.data)), points_num);
}
} // namespace blender::bke
/* ------------------------------------------------------------------- */
/** \name Grease Pencil kernel functions
* \{ */

View File

@ -172,6 +172,10 @@ static void find_used_vertex_groups(const bGPDframe &gpf,
Span<MDeformVert> dverts = {gps->dvert, gps->totpoints};
for (const MDeformVert &dvert : dverts) {
for (const MDeformWeight &weight : Span<MDeformWeight>{dvert.dw, dvert.totweight}) {
if (weight.def_nr >= dvert.totweight) {
/* Ignore invalid deform weight group indices. */
continue;
}
is_group_used[weight.def_nr] = true;
}
}
@ -362,6 +366,10 @@ void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
dst_dvert.dw = static_cast<MDeformWeight *>(MEM_dupallocN(src_dvert.dw));
const MutableSpan<MDeformWeight> vertex_weights = {dst_dvert.dw, dst_dvert.totweight};
for (MDeformWeight &weight : vertex_weights) {
if (weight.def_nr >= dst_dvert.totweight) {
/* Ignore invalid deform weight group indices. */
continue;
}
/* Map def_nr to the reduced vertex group list. */
weight.def_nr = stroke_def_nr_map[weight.def_nr];
}
@ -784,10 +792,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 +846,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 +1848,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 +2343,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 +2370,6 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
case eGpencilModifierType_Simplify:
legacy_object_modifier_simplify(object, *gpd_md);
break;
case eGpencilModifierType_Texture:
break;
}

View File

@ -457,9 +457,7 @@ static ImageGPUTextures image_get_gpu_texture(Image *ima,
if (GPU_mipmap_enabled()) {
GPU_texture_update_mipmap_chain(*tex);
if (ima) {
ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
}
ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
GPU_texture_mipmap_mode(*tex, true, true);
}
else {
@ -624,8 +622,8 @@ void BKE_image_free_old_gputextures(Main *bmain)
/** \name Paint Update
* \{ */
static ImBuf *update_do_scale(uchar *rect,
float *rect_float,
static ImBuf *update_do_scale(const uchar *rect,
const float *rect_float,
int *x,
int *y,
int *w,
@ -664,8 +662,8 @@ static ImBuf *update_do_scale(uchar *rect,
}
static void gpu_texture_update_scaled(GPUTexture *tex,
uchar *rect,
float *rect_float,
const uchar *rect,
const float *rect_float,
int full_w,
int full_h,
int x,

View File

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

View File

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

View File

@ -1852,7 +1852,7 @@ void BKE_linestyle_modifier_list_color_ramps(FreestyleLineStyle *linestyle, List
}
std::optional<std::string> BKE_linestyle_path_to_color_ramp(FreestyleLineStyle *linestyle,
ColorBand *color_ramp)
const ColorBand *color_ramp)
{
bool found = false;

View File

@ -340,7 +340,10 @@ void BKE_mask_layer_unique_name(Mask *mask, MaskLayer *masklay)
sizeof(masklay->name));
}
void BKE_mask_layer_rename(Mask *mask, MaskLayer *masklay, char *oldname, char *newname)
void BKE_mask_layer_rename(Mask *mask,
MaskLayer *masklay,
const char *oldname,
const char *newname)
{
STRNCPY(masklay->name, newname);

View File

@ -1158,6 +1158,19 @@ static void polygonize(PROCESS *process)
}
}
static bool object_has_zero_axis_matrix(const Object *bob)
{
if (has_zero_axis_m4(bob->object_to_world().ptr())) {
return true;
}
for (Object *pob = bob->parent; pob; pob = pob->parent) {
if (has_zero_axis_m4(pob->object_to_world().ptr())) {
return true;
}
}
return false;
}
/**
* Iterates over ALL objects in the scene and all of its sets, including
* making all duplis (not only meta-elements). Copies meta-elements to #process.mainb array.
@ -1168,19 +1181,15 @@ static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Obje
Scene *sce_iter = scene;
Base *base;
Object *bob;
MetaBall *mb;
const MetaElem *ml;
float obinv[4][4], obmat[4][4];
uint i;
int obnr, zero_size = 0;
int obnr;
char obname[MAX_ID_NAME];
SceneBaseIter iter;
const eEvaluationMode deg_eval_mode = DEG_get_mode(depsgraph);
const short parenting_dupli_transflag = (OB_DUPLIFACES | OB_DUPLIVERTS);
copy_m4_m4(
obmat,
ob->object_to_world().ptr()); /* to cope with duplicators from BKE_scene_base_iter_next */
/* Copy object matrices to cope with duplicators from #BKE_scene_base_iter_next. */
float obinv[4][4], obmat[4][4];
copy_m4_m4(obmat, ob->object_to_world().ptr());
invert_m4_m4(obinv, ob->object_to_world().ptr());
BLI_string_split_name_number(ob->id.name + 2, '.', obname, &obnr);
@ -1188,187 +1197,154 @@ static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Obje
/* make main array */
BKE_scene_base_iter_next(depsgraph, &iter, &sce_iter, 0, nullptr, nullptr);
while (BKE_scene_base_iter_next(depsgraph, &iter, &sce_iter, 1, &base, &bob)) {
if (bob->type == OB_MBALL) {
zero_size = 0;
ml = nullptr;
if (bob->type != OB_MBALL) {
continue;
}
/* If this metaball is the original that's used for duplication, only have it visible when
* the instancer is visible too. */
if ((base->flag_legacy & OB_FROMDUPLI) == 0 && ob->parent != nullptr &&
(ob->parent->transflag & parenting_dupli_transflag) != 0 &&
(BKE_object_visibility(ob->parent, deg_eval_mode) & OB_VISIBLE_SELF) == 0)
{
/* If this metaball is the original that's used for duplication, only have it visible when
* the instancer is visible too. */
if ((base->flag_legacy & OB_FROMDUPLI) == 0 && ob->parent != nullptr &&
(ob->parent->transflag & parenting_dupli_transflag) != 0 &&
(BKE_object_visibility(ob->parent, deg_eval_mode) & OB_VISIBLE_SELF) == 0)
{
continue;
}
if (bob == ob && (base->flag_legacy & OB_FROMDUPLI) == 0) {
/* Pass. */
}
else {
char name[MAX_ID_NAME];
int nr;
BLI_string_split_name_number(bob->id.name + 2, '.', name, &nr);
if (!STREQ(obname, name)) {
/* Not part of the mother-ball, continue. */
continue;
}
}
if (bob == ob && (base->flag_legacy & OB_FROMDUPLI) == 0) {
mb = static_cast<MetaBall *>(ob->data);
/* When metaball object has zero scale, then MetaElem to this MetaBall
* will not be put to `mainb` array. */
if (object_has_zero_axis_matrix(bob)) {
continue;
}
if (mb->editelems) {
ml = static_cast<const MetaElem *>(mb->editelems->first);
}
else {
ml = static_cast<const MetaElem *>(mb->elems.first);
}
const MetaBall *mb = static_cast<MetaBall *>(bob->data);
LISTBASE_FOREACH (const MetaElem *, ml, (mb->editelems ? mb->editelems : &mb->elems)) {
if (ml->flag & MB_HIDE) {
continue;
}
float pos[4][4], rot[4][4];
float expx, expy, expz;
blender::float3 tempmin, tempmax;
/* make a copy because of duplicates */
MetaElem *new_ml = static_cast<MetaElem *>(
BLI_memarena_alloc(process->pgn_elements, sizeof(MetaElem)));
*(new_ml) = *ml;
new_ml->bb = static_cast<BoundBox *>(
BLI_memarena_alloc(process->pgn_elements, sizeof(BoundBox)));
new_ml->mat = static_cast<float *>(
BLI_memarena_alloc(process->pgn_elements, sizeof(float[4][4])));
new_ml->imat = static_cast<float *>(
BLI_memarena_alloc(process->pgn_elements, sizeof(float[4][4])));
/* too big stiffness seems only ugly due to linear interpolation
* no need to have possibility for too big stiffness */
if (ml->s > 10.0f) {
new_ml->s = 10.0f;
}
else {
char name[MAX_ID_NAME];
int nr;
BLI_string_split_name_number(bob->id.name + 2, '.', name, &nr);
if (STREQ(obname, name)) {
mb = static_cast<MetaBall *>(bob->data);
if (mb->editelems) {
ml = static_cast<const MetaElem *>(mb->editelems->first);
}
else {
ml = static_cast<const MetaElem *>(mb->elems.first);
}
}
new_ml->s = ml->s;
}
/* when metaball object has zero scale, then MetaElem to this MetaBall
* will not be put to mainb array */
if (has_zero_axis_m4(bob->object_to_world().ptr())) {
zero_size = 1;
}
else if (bob->parent) {
Object *pob = bob->parent;
while (pob) {
if (has_zero_axis_m4(pob->object_to_world().ptr())) {
zero_size = 1;
break;
}
pob = pob->parent;
}
/* if metaball is negative, set stiffness negative */
if (new_ml->flag & MB_NEGATIVE) {
new_ml->s = -new_ml->s;
}
if (zero_size) {
while (ml) {
ml = ml->next;
}
/* Translation of MetaElem */
unit_m4(pos);
pos[3][0] = ml->x;
pos[3][1] = ml->y;
pos[3][2] = ml->z;
/* Rotation of MetaElem is stored in quat */
quat_to_mat4(rot, ml->quat);
/* Matrix multiply is as follows:
* basis object space ->
* world ->
* ml object space ->
* position ->
* rotation ->
* ml local space
*/
mul_m4_series((float(*)[4])new_ml->mat, obinv, bob->object_to_world().ptr(), pos, rot);
/* ml local space -> basis object space */
invert_m4_m4((float(*)[4])new_ml->imat, (float(*)[4])new_ml->mat);
/* rad2 is inverse of squared radius */
new_ml->rad2 = 1 / (ml->rad * ml->rad);
/* initial dimensions = radius */
expx = ml->rad;
expy = ml->rad;
expz = ml->rad;
switch (ml->type) {
case MB_BALL:
break;
case MB_CUBE: /* cube is "expanded" by expz, expy and expx */
expz += ml->expz;
ATTR_FALLTHROUGH;
case MB_PLANE: /* plane is "expanded" by expy and expx */
expy += ml->expy;
ATTR_FALLTHROUGH;
case MB_TUBE: /* tube is "expanded" by expx */
expx += ml->expx;
break;
case MB_ELIPSOID: /* ellipsoid is "stretched" by exp* */
expx *= ml->expx;
expy *= ml->expy;
expz *= ml->expz;
break;
}
else {
while (ml) {
if (!(ml->flag & MB_HIDE)) {
float pos[4][4], rot[4][4];
float expx, expy, expz;
blender::float3 tempmin, tempmax;
MetaElem *new_ml;
/* untransformed Bounding Box of MetaElem */
/* TODO: its possible the elem type has been changed and the exp*
* values can use a fallback. */
copy_v3_fl3(new_ml->bb->vec[0], -expx, -expy, -expz); /* 0 */
copy_v3_fl3(new_ml->bb->vec[1], +expx, -expy, -expz); /* 1 */
copy_v3_fl3(new_ml->bb->vec[2], +expx, +expy, -expz); /* 2 */
copy_v3_fl3(new_ml->bb->vec[3], -expx, +expy, -expz); /* 3 */
copy_v3_fl3(new_ml->bb->vec[4], -expx, -expy, +expz); /* 4 */
copy_v3_fl3(new_ml->bb->vec[5], +expx, -expy, +expz); /* 5 */
copy_v3_fl3(new_ml->bb->vec[6], +expx, +expy, +expz); /* 6 */
copy_v3_fl3(new_ml->bb->vec[7], -expx, +expy, +expz); /* 7 */
/* make a copy because of duplicates */
new_ml = static_cast<MetaElem *>(
BLI_memarena_alloc(process->pgn_elements, sizeof(MetaElem)));
*(new_ml) = *ml;
new_ml->bb = static_cast<BoundBox *>(
BLI_memarena_alloc(process->pgn_elements, sizeof(BoundBox)));
new_ml->mat = static_cast<float *>(
BLI_memarena_alloc(process->pgn_elements, sizeof(float[4][4])));
new_ml->imat = static_cast<float *>(
BLI_memarena_alloc(process->pgn_elements, sizeof(float[4][4])));
/* too big stiffness seems only ugly due to linear interpolation
* no need to have possibility for too big stiffness */
if (ml->s > 10.0f) {
new_ml->s = 10.0f;
}
else {
new_ml->s = ml->s;
}
/* if metaball is negative, set stiffness negative */
if (new_ml->flag & MB_NEGATIVE) {
new_ml->s = -new_ml->s;
}
/* Translation of MetaElem */
unit_m4(pos);
pos[3][0] = ml->x;
pos[3][1] = ml->y;
pos[3][2] = ml->z;
/* Rotation of MetaElem is stored in quat */
quat_to_mat4(rot, ml->quat);
/* Matrix multiply is as follows:
* basis object space ->
* world ->
* ml object space ->
* position ->
* rotation ->
* ml local space
*/
mul_m4_series((float(*)[4])new_ml->mat, obinv, bob->object_to_world().ptr(), pos, rot);
/* ml local space -> basis object space */
invert_m4_m4((float(*)[4])new_ml->imat, (float(*)[4])new_ml->mat);
/* rad2 is inverse of squared radius */
new_ml->rad2 = 1 / (ml->rad * ml->rad);
/* initial dimensions = radius */
expx = ml->rad;
expy = ml->rad;
expz = ml->rad;
switch (ml->type) {
case MB_BALL:
break;
case MB_CUBE: /* cube is "expanded" by expz, expy and expx */
expz += ml->expz;
ATTR_FALLTHROUGH;
case MB_PLANE: /* plane is "expanded" by expy and expx */
expy += ml->expy;
ATTR_FALLTHROUGH;
case MB_TUBE: /* tube is "expanded" by expx */
expx += ml->expx;
break;
case MB_ELIPSOID: /* ellipsoid is "stretched" by exp* */
expx *= ml->expx;
expy *= ml->expy;
expz *= ml->expz;
break;
}
/* untransformed Bounding Box of MetaElem */
/* TODO: its possible the elem type has been changed and the exp*
* values can use a fallback. */
copy_v3_fl3(new_ml->bb->vec[0], -expx, -expy, -expz); /* 0 */
copy_v3_fl3(new_ml->bb->vec[1], +expx, -expy, -expz); /* 1 */
copy_v3_fl3(new_ml->bb->vec[2], +expx, +expy, -expz); /* 2 */
copy_v3_fl3(new_ml->bb->vec[3], -expx, +expy, -expz); /* 3 */
copy_v3_fl3(new_ml->bb->vec[4], -expx, -expy, +expz); /* 4 */
copy_v3_fl3(new_ml->bb->vec[5], +expx, -expy, +expz); /* 5 */
copy_v3_fl3(new_ml->bb->vec[6], +expx, +expy, +expz); /* 6 */
copy_v3_fl3(new_ml->bb->vec[7], -expx, +expy, +expz); /* 7 */
/* Transformation of meta-elem bounding-box. */
for (i = 0; i < 8; i++) {
mul_m4_v3((float(*)[4])new_ml->mat, new_ml->bb->vec[i]);
}
/* Find max and min of transformed bounding-box. */
INIT_MINMAX(tempmin, tempmax);
for (i = 0; i < 8; i++) {
blender::math::min_max(blender::float3(new_ml->bb->vec[i]), tempmin, tempmax);
}
/* Set only point 0 and 6 - AABB of meta-elem. */
copy_v3_v3(new_ml->bb->vec[0], tempmin);
copy_v3_v3(new_ml->bb->vec[6], tempmax);
/* add new_ml to mainb[] */
if (UNLIKELY(process->totelem == process->mem)) {
process->mem = process->mem * 2 + 10;
process->mainb = static_cast<MetaElem **>(
MEM_reallocN(process->mainb, sizeof(MetaElem *) * process->mem));
}
process->mainb[process->totelem++] = new_ml;
}
ml = ml->next;
}
/* Transformation of meta-elem bounding-box. */
for (uint i = 0; i < 8; i++) {
mul_m4_v3((float(*)[4])new_ml->mat, new_ml->bb->vec[i]);
}
/* Find max and min of transformed bounding-box. */
INIT_MINMAX(tempmin, tempmax);
for (uint i = 0; i < 8; i++) {
blender::math::min_max(blender::float3(new_ml->bb->vec[i]), tempmin, tempmax);
}
/* Set only point 0 and 6 - AABB of meta-elem. */
copy_v3_v3(new_ml->bb->vec[0], tempmin);
copy_v3_v3(new_ml->bb->vec[6], tempmax);
/* add new_ml to mainb[] */
if (UNLIKELY(process->totelem == process->mem)) {
process->mem = process->mem * 2 + 10;
process->mainb = static_cast<MetaElem **>(
MEM_reallocN(process->mainb, sizeof(MetaElem *) * process->mem));
}
process->mainb[process->totelem++] = new_ml;
}
}
@ -1376,7 +1352,7 @@ static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Obje
if (process->totelem > 0) {
copy_v3_v3(process->allbb.min, process->mainb[0]->bb->vec[0]);
copy_v3_v3(process->allbb.max, process->mainb[0]->bb->vec[6]);
for (i = 1; i < process->totelem; i++) {
for (uint i = 1; i < process->totelem; i++) {
make_box_union(process->mainb[i]->bb, &process->allbb, &process->allbb);
}
}

View File

@ -113,7 +113,6 @@ static void mesh_copy_data(Main *bmain,
mesh_dst->runtime = new blender::bke::MeshRuntime();
mesh_dst->runtime->deformed_only = mesh_src->runtime->deformed_only;
mesh_dst->runtime->wrapper_type = mesh_src->runtime->wrapper_type;
mesh_dst->runtime->wrapper_type_finalize = mesh_src->runtime->wrapper_type_finalize;
mesh_dst->runtime->subsurf_runtime_data = mesh_src->runtime->subsurf_runtime_data;
mesh_dst->runtime->cd_mask_extra = mesh_src->runtime->cd_mask_extra;
/* Copy face dot tags and edge tags, since meshes may be duplicated after a subsurf modifier or

View File

@ -44,16 +44,16 @@ static void reserve_hash_maps(const Mesh &mesh,
edge_maps, [&](EdgeMap &edge_map) { edge_map.reserve(totedge_guess / edge_maps.size()); });
}
static void add_existing_edges_to_hash_maps(Mesh &mesh,
MutableSpan<EdgeMap> edge_maps,
uint32_t parallel_mask)
static void add_existing_edges_to_hash_maps(const Mesh &mesh,
const uint32_t parallel_mask,
MutableSpan<EdgeMap> edge_maps)
{
/* Assume existing edges are valid. */
const Span<int2> edges = mesh.edges();
threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
const int task_index = &edge_map - edge_maps.data();
for (const int2 &edge : edges) {
OrderedEdge ordered_edge{edge[0], edge[1]};
const OrderedEdge ordered_edge(edge[0], edge[1]);
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.add_new(ordered_edge, {&edge});
@ -62,27 +62,27 @@ static void add_existing_edges_to_hash_maps(Mesh &mesh,
});
}
static void add_face_edges_to_hash_maps(Mesh &mesh,
MutableSpan<EdgeMap> edge_maps,
uint32_t parallel_mask)
static void add_face_edges_to_hash_maps(const Mesh &mesh,
const uint32_t parallel_mask,
MutableSpan<EdgeMap> edge_maps)
{
const OffsetIndices faces = mesh.faces();
const OffsetIndices<int> faces = mesh.faces();
const Span<int> corner_verts = mesh.corner_verts();
threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
const int task_index = &edge_map - edge_maps.data();
for (const int i : faces.index_range()) {
const Span<int> face_verts = corner_verts.slice(faces[i]);
int vert_prev = face_verts.last();
for (const int vert : face_verts) {
for (const int face_i : faces.index_range()) {
const IndexRange face = faces[face_i];
for (const int corner : face) {
const int vert = corner_verts[corner];
const int vert_prev = corner_verts[bke::mesh::face_corner_prev(face, corner)];
/* Can only be the same when the mesh data is invalid. */
if (vert_prev != vert) {
OrderedEdge ordered_edge{vert_prev, vert};
const OrderedEdge ordered_edge(vert_prev, vert);
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & edge_hash_2(ordered_edge))) {
edge_map.lookup_or_add(ordered_edge, {nullptr});
}
}
vert_prev = vert;
}
}
});
@ -94,16 +94,16 @@ static void serialize_and_initialize_deduplicated_edges(MutableSpan<EdgeMap> edg
/* All edges are distributed in the hash tables now. They have to be serialized into a single
* array below. To be able to parallelize this, we have to compute edge index offsets for each
* map. */
Array<int> edge_index_offsets(edge_maps.size());
edge_index_offsets[0] = 0;
for (const int i : IndexRange(edge_maps.size() - 1)) {
edge_index_offsets[i + 1] = edge_index_offsets[i] + edge_maps[i].size();
Array<int> edge_sizes(edge_maps.size() + 1);
for (const int i : edge_maps.index_range()) {
edge_sizes[i] = edge_maps[i].size();
}
const OffsetIndices<int> edge_offsets = offset_indices::accumulate_counts_to_offsets(edge_sizes);
threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
const int task_index = &edge_map - edge_maps.data();
int new_edge_index = edge_index_offsets[task_index];
int new_edge_index = edge_offsets[task_index].first();
for (EdgeMap::MutableItem item : edge_map.items()) {
int2 &new_edge = new_edges[new_edge_index];
const int2 *orig_edge = item.value.original_edge;
@ -113,8 +113,7 @@ static void serialize_and_initialize_deduplicated_edges(MutableSpan<EdgeMap> edg
}
else {
/* Initialize new edge. */
new_edge[0] = item.key.v_low;
new_edge[1] = item.key.v_high;
new_edge = int2(item.key.v_low, item.key.v_high);
}
item.value.index = new_edge_index;
new_edge_index++;
@ -131,14 +130,13 @@ static void update_edge_indices_in_face_loops(const OffsetIndices<int> faces,
threading::parallel_for(faces.index_range(), 100, [&](IndexRange range) {
for (const int face_index : range) {
const IndexRange face = faces[face_index];
int prev_corner = face.last();
for (const int next_corner : face) {
const int vert = corner_verts[next_corner];
const int vert_prev = corner_verts[prev_corner];
for (const int corner : face) {
const int vert = corner_verts[corner];
const int vert_prev = corner_verts[bke::mesh::face_corner_next(face, corner)];
int edge_index;
if (vert_prev != vert) {
OrderedEdge ordered_edge{vert_prev, vert};
const OrderedEdge ordered_edge(vert_prev, vert);
/* Double lookup: First find the map that contains the edge, then lookup the edge. */
const EdgeMap &edge_map = edge_maps[parallel_mask & edge_hash_2(ordered_edge)];
edge_index = edge_map.lookup(ordered_edge).index;
@ -149,8 +147,7 @@ static void update_edge_indices_in_face_loops(const OffsetIndices<int> faces,
* #76514. */
edge_index = 0;
}
corner_edges[prev_corner] = edge_index;
prev_corner = next_corner;
corner_edges[corner] = edge_index;
}
}
});
@ -187,21 +184,20 @@ void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, const bool select_new
/* Add all edges. */
if (keep_existing_edges) {
calc_edges::add_existing_edges_to_hash_maps(mesh, edge_maps, parallel_mask);
calc_edges::add_existing_edges_to_hash_maps(mesh, parallel_mask, edge_maps);
}
calc_edges::add_face_edges_to_hash_maps(mesh, edge_maps, parallel_mask);
calc_edges::add_face_edges_to_hash_maps(mesh, parallel_mask, edge_maps);
/* Compute total number of edges. */
int new_totedge = 0;
for (calc_edges::EdgeMap &edge_map : edge_maps) {
for (const calc_edges::EdgeMap &edge_map : edge_maps) {
new_totedge += edge_map.size();
}
/* Create new edges. */
MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.add<int>(".corner_edge", AttrDomain::Corner, AttributeInitConstruct());
MutableSpan<int2> new_edges{
static_cast<int2 *>(MEM_calloc_arrayN(new_totedge, sizeof(int2), __func__)), new_totedge};
MutableSpan<int2> new_edges(MEM_cnew_array<int2>(new_totedge, __func__), new_totedge);
calc_edges::serialize_and_initialize_deduplicated_edges(edge_maps, new_edges);
calc_edges::update_edge_indices_in_face_loops(
mesh.faces(), mesh.corner_verts(), edge_maps, parallel_mask, mesh.corner_edges_for_write());
@ -219,7 +215,7 @@ void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, const bool select_new
if (select_edge) {
int new_edge_index = 0;
for (const calc_edges::EdgeMap &edge_map : edge_maps) {
for (calc_edges::EdgeMap::Item item : edge_map.items()) {
for (const calc_edges::EdgeMap::Item item : edge_map.items()) {
if (item.value.original_edge == nullptr) {
select_edge.span[new_edge_index] = true;
}

View File

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

View File

@ -45,8 +45,7 @@ void BKE_mesh_foreach_mapped_vert(
const blender::Span<blender::float3> positions = mesh->runtime->edit_data->vert_positions;
blender::Span<blender::float3> vert_normals;
if (flag & MESH_FOREACH_USE_NORMAL) {
BKE_editmesh_cache_ensure_vert_normals(*em, *mesh->runtime->edit_data);
vert_normals = mesh->runtime->edit_data->vert_normals;
vert_normals = BKE_editmesh_cache_ensure_vert_normals(*em, *mesh->runtime->edit_data);
}
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
const float *no = (flag & MESH_FOREACH_USE_NORMAL) ? &vert_normals[i].x : nullptr;
@ -236,18 +235,16 @@ void BKE_mesh_foreach_mapped_face_center(
if (mesh->runtime->edit_mesh != nullptr && mesh->runtime->edit_data != nullptr) {
BMEditMesh *em = mesh->runtime->edit_mesh;
BMesh *bm = em->bm;
blender::Span<blender::float3> face_centers;
blender::Span<blender::float3> face_normals;
BMFace *efa;
BMIter iter;
int i;
BKE_editmesh_cache_ensure_face_centers(*em, *mesh->runtime->edit_data);
face_centers = mesh->runtime->edit_data->face_centers; /* always set */
const Span<float3> face_centers = BKE_editmesh_cache_ensure_face_centers(
*em, *mesh->runtime->edit_data);
Span<float3> face_normals;
if (flag & MESH_FOREACH_USE_NORMAL) {
BKE_editmesh_cache_ensure_face_normals(*em, *mesh->runtime->edit_data);
face_normals = mesh->runtime->edit_data->face_normals; /* maybe nullptr */
face_normals = BKE_editmesh_cache_ensure_face_normals(*em, *mesh->runtime->edit_data);
}
if (!face_normals.is_empty()) {

View File

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

View File

@ -129,10 +129,6 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
mesh->runtime->is_original_bmesh = false;
}
if (mesh->runtime->wrapper_type_finalize) {
BKE_mesh_wrapper_deferred_finalize_mdata(mesh);
}
mesh->runtime->edit_data.reset();
break;
}
@ -165,8 +161,8 @@ Span<float3> BKE_mesh_wrapper_face_normals(Mesh *mesh)
{
switch (mesh->runtime->wrapper_type) {
case ME_WRAPPER_TYPE_BMESH:
BKE_editmesh_cache_ensure_face_normals(*mesh->runtime->edit_mesh, *mesh->runtime->edit_data);
return mesh->runtime->edit_data->face_normals;
return BKE_editmesh_cache_ensure_face_normals(*mesh->runtime->edit_mesh,
*mesh->runtime->edit_data);
case ME_WRAPPER_TYPE_MDATA:
case ME_WRAPPER_TYPE_SUBD:
return mesh->face_normals();
@ -179,10 +175,10 @@ void BKE_mesh_wrapper_tag_positions_changed(Mesh *mesh)
{
switch (mesh->runtime->wrapper_type) {
case ME_WRAPPER_TYPE_BMESH:
if (mesh->runtime->edit_data) {
mesh->runtime->edit_data->vert_normals = {};
mesh->runtime->edit_data->face_centers = {};
mesh->runtime->edit_data->face_normals = {};
if (blender::bke::EditMeshData *edit_data = mesh->runtime->edit_data.get()) {
edit_data->vert_normals = {};
edit_data->face_centers = {};
edit_data->face_normals = {};
}
break;
case ME_WRAPPER_TYPE_MDATA:

View File

@ -1804,7 +1804,7 @@ void BKE_movieclip_build_proxy_frame(MovieClip *clip,
int clip_flag,
MovieDistortion *distortion,
int cfra,
int *build_sizes,
const int *build_sizes,
int build_count,
bool undistorted)
{
@ -1845,7 +1845,7 @@ void BKE_movieclip_build_proxy_frame_for_ibuf(MovieClip *clip,
ImBuf *ibuf,
MovieDistortion *distortion,
int cfra,
int *build_sizes,
const int *build_sizes,
int build_count,
bool undistorted)
{

View File

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

View File

@ -1455,7 +1455,7 @@ bool BKE_object_modifier_stack_copy(Object *ob_dst,
const int flag_subdata)
{
if ((ob_dst->type == OB_GPENCIL_LEGACY) != (ob_src->type == OB_GPENCIL_LEGACY)) {
BLI_assert_msg(0,
BLI_assert_msg(false,
"Trying to copy a modifier stack between a GPencil object and another type.");
return false;
}
@ -1463,8 +1463,9 @@ bool BKE_object_modifier_stack_copy(Object *ob_dst,
if (!BLI_listbase_is_empty(&ob_dst->modifiers) ||
!BLI_listbase_is_empty(&ob_dst->greasepencil_modifiers))
{
BLI_assert(
!"Trying to copy a modifier stack into an object having a non-empty modifier stack.");
BLI_assert_msg(
false,
"Trying to copy a modifier stack into an object having a non-empty modifier stack.");
return false;
}

View File

@ -444,17 +444,17 @@ static void make_child_duplis(const DupliContext *ctx,
static const Mesh *mesh_data_from_duplicator_object(Object *ob,
BMEditMesh **r_em,
const float (**r_vert_coords)[3],
const float (**r_vert_normals)[3])
Span<float3> *r_vert_coords,
Span<float3> *r_vert_normals)
{
/* Gather mesh info. */
BMEditMesh *em = BKE_editmesh_from_object(ob);
const Mesh *mesh_eval;
*r_em = nullptr;
*r_vert_coords = nullptr;
*r_vert_coords = {};
if (r_vert_normals != nullptr) {
*r_vert_normals = nullptr;
*r_vert_normals = {};
}
/* We do not need any render-specific handling anymore, depsgraph takes care of that. */
@ -473,10 +473,9 @@ static const Mesh *mesh_data_from_duplicator_object(Object *ob,
mesh_eval = nullptr;
if ((emd != nullptr) && !emd->vert_positions.is_empty()) {
*r_vert_coords = reinterpret_cast<const float(*)[3]>(emd->vert_positions.data());
*r_vert_coords = emd->vert_positions;
if (r_vert_normals != nullptr) {
BKE_editmesh_cache_ensure_vert_normals(*em, *emd);
*r_vert_normals = reinterpret_cast<const float(*)[3]>(emd->vert_normals.data());
*r_vert_normals = BKE_editmesh_cache_ensure_vert_normals(*em, *emd);
}
}
}
@ -563,9 +562,9 @@ struct VertexDupliData_EditMesh {
BMEditMesh *em;
/* Can be nullptr. */
const float (*vert_positions_deform)[3];
const float (*vert_normals_deform)[3];
/* Can be empty. */
Span<float3> vert_positions_deform;
Span<float3> vert_normals_deform;
/**
* \note The edit-mesh may assign #DupliObject.orco in cases when a regular mesh wouldn't.
@ -582,8 +581,8 @@ struct VertexDupliData_EditMesh {
* currently this is copied from a `short[3]` normal without division.
* Can be null when \a use_rotation is false.
*/
static void get_duplivert_transform(const float co[3],
const float no[3],
static void get_duplivert_transform(const float3 &co,
const float3 &no,
const bool use_rotation,
const short axis,
const short upflag,
@ -609,8 +608,8 @@ static DupliObject *vertex_dupli(const DupliContext *ctx,
Object *inst_ob,
const float child_imat[4][4],
int index,
const float co[3],
const float no[3],
const float3 &co,
const float3 &no,
const bool use_rotation)
{
/* `obmat` is transform to vertex. */
@ -681,14 +680,13 @@ static void make_child_duplis_verts_from_editmesh(const DupliContext *ctx,
BMIter iter;
int i;
const float(*vert_positions_deform)[3] = vdd->vert_positions_deform;
const float(*vert_normals_deform)[3] = vdd->vert_normals_deform;
const Span<float3> vert_positions_deform = vdd->vert_positions_deform;
const Span<float3> vert_normals_deform = vdd->vert_normals_deform;
BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) {
const float *co, *no;
if (vert_positions_deform != nullptr) {
float3 co, no;
if (!vert_positions_deform.is_empty()) {
co = vert_positions_deform[i];
no = vert_normals_deform ? vert_normals_deform[i] : nullptr;
no = !vert_normals_deform.is_empty() ? vert_normals_deform[i] : float3(0);
}
else {
co = v->co;
@ -709,8 +707,8 @@ static void make_duplis_verts(const DupliContext *ctx)
/* Gather mesh info. */
BMEditMesh *em = nullptr;
const float(*vert_positions_deform)[3] = nullptr;
const float(*vert_normals_deform)[3] = nullptr;
Span<float3> vert_positions_deform;
Span<float3> vert_normals_deform;
const Mesh *mesh_eval = mesh_data_from_duplicator_object(
parent, &em, &vert_positions_deform, use_rotation ? &vert_normals_deform : nullptr);
if (em == nullptr && mesh_eval == nullptr) {
@ -725,7 +723,7 @@ static void make_duplis_verts(const DupliContext *ctx)
vdd.em = em;
vdd.vert_positions_deform = vert_positions_deform;
vdd.vert_normals_deform = vert_normals_deform;
vdd.has_orco = (vert_positions_deform != nullptr);
vdd.has_orco = !vert_positions_deform.is_empty();
make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_editmesh);
}
@ -1080,8 +1078,8 @@ struct FaceDupliData_EditMesh {
bool has_orco, has_uvs;
int cd_loop_uv_offset;
/* Can be nullptr. */
const float (*vert_positions_deform)[3];
/* Can be empty. */
Span<float3> vert_positions_deform;
};
static void get_dupliface_transform_from_coords(Span<float3> coords,
@ -1186,7 +1184,7 @@ static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
/* Mesh variables. */
BMFace *f,
const float (*vert_positions_deform)[3])
const Span<float3> vert_positions_deform)
{
const int coords_len = f->len;
Array<float3, 64> coords(coords_len);
@ -1194,7 +1192,7 @@ static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
BMLoop *l_first, *l_iter;
int i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
if (vert_positions_deform != nullptr) {
if (!vert_positions_deform.is_empty()) {
do {
copy_v3_v3(coords[i++], vert_positions_deform[BM_elem_index_get(l_iter->v)]);
} while ((l_iter = l_iter->next) != l_first);
@ -1263,9 +1261,9 @@ static void make_child_duplis_faces_from_editmesh(const DupliContext *ctx,
BMIter iter;
const bool use_scale = fdd->params.use_scale;
const float(*vert_positions_deform)[3] = fdd->vert_positions_deform;
const Span<float3> vert_positions_deform = fdd->vert_positions_deform;
BLI_assert((vert_positions_deform == nullptr) || (em->bm->elem_index_dirty & BM_VERT) == 0);
BLI_assert(vert_positions_deform.is_empty() || (em->bm->elem_index_dirty & BM_VERT) == 0);
invert_m4_m4(inst_ob->runtime->world_to_object.ptr(), inst_ob->object_to_world().ptr());
/* Relative transform from parent to child space. */
@ -1296,7 +1294,7 @@ static void make_duplis_faces(const DupliContext *ctx)
/* Gather mesh info. */
BMEditMesh *em = nullptr;
const float(*vert_positions_deform)[3] = nullptr;
Span<float3> vert_positions_deform;
const Mesh *mesh_eval = mesh_data_from_duplicator_object(
parent, &em, &vert_positions_deform, nullptr);
if (em == nullptr && mesh_eval == nullptr) {
@ -1311,7 +1309,7 @@ static void make_duplis_faces(const DupliContext *ctx)
fdd.params = fdd_params;
fdd.em = em;
fdd.vert_positions_deform = vert_positions_deform;
fdd.has_orco = (vert_positions_deform != nullptr);
fdd.has_orco = !vert_positions_deform.is_empty();
fdd.has_uvs = (uv_idx != -1);
fdd.cd_loop_uv_offset = (uv_idx != -1) ?
CustomData_get_n_offset(&em->bm->ldata, CD_PROP_FLOAT2, uv_idx) :

View File

@ -367,7 +367,7 @@ int BKE_packedfile_write_to_file(ReportList *reports,
enum ePF_FileCompare BKE_packedfile_compare_to_file(const char *ref_file_name,
const char *filepath_rel,
PackedFile *pf)
const PackedFile *pf)
{
BLI_stat_t st;
enum ePF_FileCompare ret_val;
@ -880,7 +880,7 @@ void BKE_packedfile_id_unpack(Main *bmain, ID *id, ReportList *reports, enum ePF
}
}
void BKE_packedfile_blend_write(BlendWriter *writer, PackedFile *pf)
void BKE_packedfile_blend_write(BlendWriter *writer, const PackedFile *pf)
{
if (pf == nullptr) {
return;

View File

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

View File

@ -795,7 +795,7 @@ static void subdiv_ccg_average_inner_face_grids(SubdivCCG &subdiv_ccg,
static void subdiv_ccg_average_grids_boundary(SubdivCCG &subdiv_ccg,
const CCGKey &key,
SubdivCCGAdjacentEdge &adjacent_edge,
const SubdivCCGAdjacentEdge &adjacent_edge,
MutableSpan<GridElementAccumulator> accumulators)
{
const int num_adjacent_faces = adjacent_edge.num_adjacent_faces;
@ -837,7 +837,7 @@ struct AverageGridsCornerData {
static void subdiv_ccg_average_grids_corners(SubdivCCG &subdiv_ccg,
const CCGKey &key,
SubdivCCGAdjacentVertex &adjacent_vertex)
const SubdivCCGAdjacentVertex &adjacent_vertex)
{
const int num_adjacent_faces = adjacent_vertex.num_adjacent_faces;
if (num_adjacent_faces == 1) {
@ -871,7 +871,7 @@ static void subdiv_ccg_average_boundaries(SubdivCCG &subdiv_ccg,
adjacent_edge_mask.foreach_segment(GrainSize(1024), [&](const IndexMaskSegment segment) {
MutableSpan<GridElementAccumulator> accumulators = all_accumulators.local();
for (const int i : segment) {
SubdivCCGAdjacentEdge &adjacent_edge = subdiv_ccg.adjacent_edges[i];
const SubdivCCGAdjacentEdge &adjacent_edge = subdiv_ccg.adjacent_edges[i];
subdiv_ccg_average_grids_boundary(subdiv_ccg, key, adjacent_edge, accumulators);
}
});
@ -883,7 +883,7 @@ static void subdiv_ccg_average_corners(SubdivCCG &subdiv_ccg,
{
using namespace blender;
adjacent_vert_mask.foreach_index(GrainSize(1024), [&](const int i) {
SubdivCCGAdjacentVertex &adjacent_vert = subdiv_ccg.adjacent_verts[i];
const SubdivCCGAdjacentVertex &adjacent_vert = subdiv_ccg.adjacent_verts[i];
subdiv_ccg_average_grids_corners(subdiv_ccg, key, adjacent_vert);
});
}
@ -1004,15 +1004,8 @@ BLI_INLINE void subdiv_ccg_neighbors_init(SubdivCCGNeighbors &neighbors,
const int num_duplicates)
{
const int size = num_unique + num_duplicates;
neighbors.size = size;
neighbors.coords.reinitialize(size);
neighbors.num_duplicates = num_duplicates;
if (size < ARRAY_SIZE(neighbors.coords_fixed)) {
neighbors.coords = neighbors.coords_fixed;
}
else {
neighbors.coords = static_cast<SubdivCCGCoord *>(
MEM_mallocN(sizeof(*neighbors.coords) * size, "SubdivCCGNeighbors.coords"));
}
}
/* Check whether given coordinate belongs to a grid corner. */
@ -1520,7 +1513,7 @@ void BKE_subdiv_ccg_neighbor_coords_get(const SubdivCCG &subdiv_ccg,
}
#ifndef NDEBUG
for (int i = 0; i < r_neighbors.size; i++) {
for (const int i : r_neighbors.coords.index_range()) {
BLI_assert(BKE_subdiv_ccg_check_coord_valid(subdiv_ccg, r_neighbors.coords[i]));
}
#endif

View File

@ -673,10 +673,10 @@ void txt_clean_text(Text *text)
}
}
int txt_get_span(TextLine *from, const TextLine *to)
int txt_get_span(const TextLine *from, const TextLine *to)
{
int ret = 0;
TextLine *tmp = from;
const TextLine *tmp = from;
if (!to || !from) {
return 0;

View File

@ -806,8 +806,9 @@ bool BKE_undosys_step_load_data_ex(UndoStack *ustack,
}
}
BLI_assert(
!"This should never be reached, either undo stack is corrupted, or code above is buggy");
BLI_assert_msg(
false,
"This should never be reached, either undo stack is corrupted, or code above is buggy");
return false;
}

View File

@ -46,6 +46,7 @@ extern "C" {
# include <libavformat/avformat.h>
# include <libavutil/buffer.h>
# include <libavutil/channel_layout.h>
# include <libavutil/cpu.h>
# include <libavutil/imgutils.h>
# include <libavutil/opt.h>
# include <libavutil/rational.h>
@ -256,14 +257,15 @@ static AVFrame *alloc_picture(AVPixelFormat pix_fmt, int width, int height)
}
/* allocate the actual picture buffer */
int size = av_image_get_buffer_size(pix_fmt, width, height, 1);
const size_t align = av_cpu_max_align();
int size = av_image_get_buffer_size(pix_fmt, width, height, align);
AVBufferRef *buf = av_buffer_alloc(size);
if (buf == nullptr) {
av_frame_free(&f);
return nullptr;
}
av_image_fill_arrays(f->data, f->linesize, buf->data, pix_fmt, width, height, 1);
av_image_fill_arrays(f->data, f->linesize, buf->data, pix_fmt, width, height, align);
f->buf[0] = buf;
f->format = pix_fmt;
f->width = width;
@ -418,15 +420,16 @@ static AVFrame *generate_video_frame(FFMpegContext *context, const ImBuf *image)
/* Copy the Blender pixels into the FFMPEG data-structure, taking care of endianness and flipping
* the image vertically. */
int linesize = rgb_frame->linesize[0];
int linesize_src = rgb_frame->width * 4;
for (int y = 0; y < height; y++) {
uint8_t *target = rgb_frame->data[0] + linesize * (height - y - 1);
const uint8_t *src = pixels + linesize * y;
const uint8_t *src = pixels + linesize_src * y;
# if ENDIAN_ORDER == L_ENDIAN
memcpy(target, src, linesize);
memcpy(target, src, linesize_src);
# elif ENDIAN_ORDER == B_ENDIAN
const uint8_t *end = src + linesize;
const uint8_t *end = src + linesize_src;
while (src != end) {
target[3] = src[0];
target[2] = src[1];

View File

@ -27,7 +27,7 @@ BArrayStore *BLI_array_store_at_size_get(struct BArrayStore_AtSize *bs_stride, i
void BLI_array_store_at_size_clear(struct BArrayStore_AtSize *bs_stride);
void BLI_array_store_at_size_calc_memory_usage(struct BArrayStore_AtSize *bs_stride,
void BLI_array_store_at_size_calc_memory_usage(const struct BArrayStore_AtSize *bs_stride,
size_t *r_size_expanded,
size_t *r_size_compacted);

View File

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

View File

@ -199,6 +199,30 @@ class ImplicitSharingMixin : public ImplicitSharingInfo {
virtual void delete_self() = 0;
};
/**
* Utility for creating an allocated shared resource, to be used like:
* `new ImplicitSharedValue<T>(args);`
*/
template<typename T> class ImplicitSharedValue : public ImplicitSharingInfo {
public:
T data;
template<typename... Args>
ImplicitSharedValue(Args &&...args) : data(std::forward<Args>(args)...)
{
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("ImplicitSharedValue");
#endif
private:
void delete_self_with_data() override
{
delete this;
}
};
/**
* Utility that contains sharing information and the data that is shared.
*/

View File

@ -8,6 +8,9 @@
* \ingroup bli
*/
#include <memory>
#include <utility>
#include "BLI_implicit_sharing.hh"
#include "BLI_struct_equality_utils.hh"
@ -132,4 +135,64 @@ template<typename T> class ImplicitSharingPtr {
}
};
/**
* Utility struct to allow used #ImplicitSharingPtr when it's necessary to type-erase the backing
* storage for user-exposed data. For example, #blender::Vector, or #std::vector might be used to
* store an implicitly shared array that is only accessed with #Span or #MutableSpan.
*
* This class handles RAII for the sharing info and the exposed data pointer.
* Retrieving the data with write access and type safety must be handled elsewhere.
*/
class ImplicitSharingPtrAndData {
public:
ImplicitSharingPtr<ImplicitSharingInfo> sharing_info;
const void *data = nullptr;
ImplicitSharingPtrAndData() = default;
ImplicitSharingPtrAndData(ImplicitSharingPtr<ImplicitSharingInfo> sharing_info, const void *data)
: sharing_info(std::move(sharing_info)), data(data)
{
}
ImplicitSharingPtrAndData(const ImplicitSharingPtrAndData &other)
: sharing_info(other.sharing_info), data(other.data)
{
}
ImplicitSharingPtrAndData(ImplicitSharingPtrAndData &&other)
: sharing_info(std::move(other.sharing_info)), data(std::exchange(other.data, nullptr))
{
}
ImplicitSharingPtrAndData &operator=(const ImplicitSharingPtrAndData &other)
{
if (this == &other) {
return *this;
}
std::destroy_at(this);
new (this) ImplicitSharingPtrAndData(other);
return *this;
}
ImplicitSharingPtrAndData &operator=(ImplicitSharingPtrAndData &&other)
{
if (this == &other) {
return *this;
}
std::destroy_at(this);
new (this) ImplicitSharingPtrAndData(std::move(other));
return *this;
}
~ImplicitSharingPtrAndData()
{
this->data = nullptr;
}
bool has_value() const
{
return this->sharing_info.has_value();
}
};
} // namespace blender

View File

@ -45,6 +45,10 @@ extern "C" {
#define BLI_STR_UTF8_DOWNWARDS_ARROW "\xe2\x86\x93"
/** u21E7: `⇧` */
#define BLI_STR_UTF8_UPWARDS_WHITE_ARROW "\xe2\x87\xa7"
/** u21FF: `⇪` For caps lock */
#define BLI_STR_UTF8_UPWARDS_UP_ARROW_FROM_BAR "\xe2\x87\xaa"
/** u2277: `≷` Greater than / Less than */
#define BLI_STR_UTF8_GREATER_THAN_OR_LESS_THAN "\xe2\x89\xb7"
/** u2303: `⌃` */
#define BLI_STR_UTF8_UP_ARROWHEAD "\xe2\x8c\x83"
/** u2318: `⌘` */
@ -67,12 +71,22 @@ extern "C" {
#define BLI_STR_UTF8_BLACK_SQUARE_FOR_STOP "\xe2\x8f\xb9"
/** u2423: `␣` */
#define BLI_STR_UTF8_OPEN_BOX "\xe2\x90\xa3"
/** u25A6: `▦` */
#define BLI_STR_UTF8_SQUARE_WITH_ORTHOGONAL_CROSSHATCH "\xe2\x96\xa6"
/** u25B8: `▸` */
#define BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE "\xe2\x96\xb8"
/** u2B7E: `⭾` */
#define BLI_STR_UTF8_HORIZONTAL_TAB_KEY "\xe2\xad\xbe"
/** u270E: `✎` Tablet Pen */
#define BLI_STR_UTF8_LOWER_RIGHT_PENCIL "\xe2\x9c\x8e"
/** u2710: `✐` Tablet Eraser */
#define BLI_STR_UTF8_UPPER_RIGHT_PENCIL "\xe2\x9c\x90"
/** u2756: `❖` */
#define BLI_STR_UTF8_BLACK_DIAMOND_MINUS_WHITE_X "\xe2\x9d\x96"
/** u29BE: `⦾` Use for 3D Mice */
#define BLI_STR_UTF8_CIRCLED_WHITE_BULLET "\xe2\xa6\xbe"
/** u2B2E: `⬮` Generic Mouse */
#define BLI_STR_UTF8_BLACK_VERTICAL_ELLIPSE "\xe2\xac\xae"
#ifdef __cplusplus
}

View File

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

View File

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

View File

@ -59,14 +59,14 @@ void BLI_array_store_at_size_clear(BArrayStore_AtSize *bs_stride)
bs_stride->stride_table_len = 0;
}
void BLI_array_store_at_size_calc_memory_usage(BArrayStore_AtSize *bs_stride,
void BLI_array_store_at_size_calc_memory_usage(const BArrayStore_AtSize *bs_stride,
size_t *r_size_expanded,
size_t *r_size_compacted)
{
size_t size_compacted = 0;
size_t size_expanded = 0;
for (int i = 0; i < bs_stride->stride_table_len; i++) {
BArrayStore *bs = bs_stride->stride_table[i];
const BArrayStore *bs = bs_stride->stride_table[i];
if (bs) {
size_compacted += BLI_array_store_calc_size_compacted_get(bs);
size_expanded += BLI_array_store_calc_size_expanded_get(bs);

View File

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

View File

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

View File

@ -11,6 +11,7 @@
# include <algorithm>
# include <fstream>
# include <functional>
# include <iostream>
# include <memory>
@ -945,7 +946,7 @@ class CoplanarClusterInfo {
return tri_cluster_[t];
}
int add_cluster(CoplanarCluster cl)
int add_cluster(const CoplanarCluster &cl)
{
int c_index = clusters_.append_and_get_index(cl);
for (int t : cl) {

View File

@ -39,7 +39,7 @@ typedef struct PolyFill {
uint edges, verts;
float min_xy[2], max_xy[2];
ushort nr;
bool f;
uchar f;
} PolyFill;
typedef struct ScanFillVertLink {
@ -52,19 +52,19 @@ typedef struct ScanFillVertLink {
#define SF_EPSILON 0.00003f
#define SF_EPSILON_SQ (SF_EPSILON * SF_EPSILON)
/** #ScanFillVert.status */
/** #ScanFillVert::f (status) */
#define SF_VERT_NEW 0 /* all new verts have this flag set */
#define SF_VERT_AVAILABLE 1 /* available - in an edge */
#define SF_VERT_ZERO_LEN 2
/** #ScanFillEdge.status */
/** #ScanFillEdge::f (status) */
/* Optionally set ScanFillEdge f to this to mark original boundary edges.
* Only needed if there are internal diagonal edges passed to BLI_scanfill_calc. */
#define SF_EDGE_NEW 0 /* all new edges have this flag set */
// #define SF_EDGE_BOUNDARY 1 /* UNUSED */
#define SF_EDGE_INTERNAL 2 /* edge is created while scan-filling */
/** #PolyFill.status */
/** #PolyFill::f (status) */
#define SF_POLY_NEW 0 /* all polys initialized to this */
#define SF_POLY_VALID 1 /* has at least 3 verts */

View File

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

View File

@ -126,7 +126,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
LinkNode *name = names;
for (; name; name = name->next) {
char *str_name = (char *)name->link;
const char *str_name = (const char *)name->link;
if (id->name[2] == str_name[0] && STREQ(str_name, id->name + 2)) {
break;
}

View File

@ -1335,8 +1335,9 @@ void blo_do_versions_290(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (relation->parentid == 0) {
BLI_assert(
!"Found a valid parent for workspace data relation, but no valid parent id.");
BLI_assert_msg(
false,
"Found a valid parent for workspace data relation, but no valid parent id.");
}
}
if (relation->parentid == 0) {

View File

@ -14,6 +14,7 @@
#include "DNA_scene_types.h"
#include "BLI_array.hh"
#include "BLI_bitmap.h"
#include "BLI_linklist_stack.h"
#include "BLI_math_base.hh"
@ -29,15 +30,18 @@
#include "intern/bmesh_private.hh"
using blender::Array;
using blender::float3;
using blender::MutableSpan;
using blender::Span;
/* Smooth angle to use when tagging edges is disabled entirely. */
#define EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS -FLT_MAX
static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3],
static void bm_edge_tag_from_smooth_and_set_sharp(Span<float3> fnos,
BMEdge *e,
const float split_angle_cos);
static void bm_edge_tag_from_smooth(const float (*fnos)[3],
BMEdge *e,
const float split_angle_cos);
static void bm_edge_tag_from_smooth(Span<float3> fnos, BMEdge *e, const float split_angle_cos);
/* -------------------------------------------------------------------- */
/** \name Update Vertex & Face Normals
@ -53,11 +57,11 @@ static void bm_edge_tag_from_smooth(const float (*fnos)[3],
struct BMVertsCalcNormalsWithCoordsData {
/* Read-only data. */
const float (*fnos)[3];
const float (*vcos)[3];
Span<float3> fnos;
Span<float3> vcos;
/* Write data. */
float (*vnos)[3];
MutableSpan<float3> vnos;
};
BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter,
@ -204,21 +208,21 @@ static void bm_vert_calc_normals_with_coords_cb(void *userdata,
}
static void bm_mesh_verts_calc_normals(BMesh *bm,
const float (*fnos)[3],
const float (*vcos)[3],
float (*vnos)[3])
const Span<float3> fnos,
const Span<float3> vcos,
MutableSpan<float3> vnos)
{
BM_mesh_elem_index_ensure(bm, BM_FACE | ((vnos || vcos) ? BM_VERT : 0));
BM_mesh_elem_index_ensure(bm, BM_FACE | ((!vnos.is_empty() || !vcos.is_empty()) ? BM_VERT : 0));
TaskParallelSettings settings;
BLI_parallel_mempool_settings_defaults(&settings);
settings.use_threading = bm->totvert >= BM_THREAD_LIMIT;
if (vcos == nullptr) {
if (vcos.is_empty()) {
BM_iter_parallel(bm, BM_VERTS_OF_MESH, bm_vert_calc_normals_cb, nullptr, &settings);
}
else {
BLI_assert(!ELEM(nullptr, fnos, vnos));
BLI_assert(!fnos.is_empty() || !vnos.is_empty());
BMVertsCalcNormalsWithCoordsData data{};
data.fnos = fnos;
data.vcos = vcos;
@ -248,7 +252,7 @@ void BM_mesh_normals_update_ex(BMesh *bm, const BMeshNormalsUpdate_Params *param
}
/* Add weighted face normals to vertices, and normalize vert normals. */
bm_mesh_verts_calc_normals(bm, nullptr, nullptr, nullptr);
bm_mesh_verts_calc_normals(bm, {}, {}, {});
}
void BM_mesh_normals_update(BMesh *bm)
@ -321,9 +325,9 @@ void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpin
* \{ */
void BM_verts_calc_normal_vcos(BMesh *bm,
const float (*fnos)[3],
const float (*vcos)[3],
float (*vnos)[3])
const Span<float3> fnos,
const Span<float3> vcos,
MutableSpan<float3> vnos)
{
/* Add weighted face normals to vertices, and normalize vert normals. */
bm_mesh_verts_calc_normals(bm, fnos, vcos, vnos);
@ -374,7 +378,7 @@ void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
* Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normal_vcos
*/
static void bm_mesh_edges_sharp_tag(BMesh *bm,
const float (*fnos)[3],
const Span<float3> fnos,
float split_angle_cos,
const bool do_sharp_edges_tag)
{
@ -382,7 +386,7 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm,
BMEdge *e;
int i;
if (fnos) {
if (!fnos.is_empty()) {
BM_mesh_elem_index_ensure(bm, BM_FACE);
}
@ -413,7 +417,7 @@ void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
return;
}
bm_mesh_edges_sharp_tag(bm, nullptr, cosf(split_angle), true);
bm_mesh_edges_sharp_tag(bm, {}, cosf(split_angle), true);
}
/** \} */
@ -467,8 +471,8 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
* \return The number of loops that were handled (for early exit when all have been handled).
*/
static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
const Span<float3> vcos,
const Span<float3> fnos,
const short (*clnors_data)[2],
const int cd_loop_clnors_offset,
const bool has_clnors,
@ -477,12 +481,12 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
/* Iterate. */
BMLoop *l_curr,
/* Result. */
float (*r_lnos)[3],
MutableSpan<float3> r_lnos,
MLoopNorSpaceArray *r_lnors_spacearr)
{
BLI_assert((bm->elem_index_dirty & BM_LOOP) == 0);
BLI_assert((fnos == nullptr) || ((bm->elem_index_dirty & BM_FACE) == 0));
BLI_assert((vcos == nullptr) || ((bm->elem_index_dirty & BM_VERT) == 0));
BLI_assert(fnos.is_empty() || ((bm->elem_index_dirty & BM_FACE) == 0));
BLI_assert(vcos.is_empty() || ((bm->elem_index_dirty & BM_VERT) == 0));
UNUSED_VARS_NDEBUG(bm);
int handled = 0;
@ -514,7 +518,8 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
* this vertex just takes its face normal.
*/
const int l_curr_index = BM_elem_index_get(l_curr);
const float *no = fnos ? fnos[BM_elem_index_get(l_curr->f)] : l_curr->f->no;
const float3 no = !fnos.is_empty() ? fnos[BM_elem_index_get(l_curr->f)] :
float3(l_curr->f->no);
copy_v3_v3(r_lnos[l_curr_index], no);
/* If needed, generate this (simple!) lnor space. */
@ -524,11 +529,12 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
{
const BMVert *v_pivot = l_curr->v;
const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
const float3 co_pivot = !vcos.is_empty() ? vcos[BM_elem_index_get(v_pivot)] :
float3(v_pivot->co);
const BMVert *v_1 = l_curr->next->v;
const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co;
const float3 co_1 = !vcos.is_empty() ? vcos[BM_elem_index_get(v_1)] : float3(v_1->co);
const BMVert *v_2 = l_curr->prev->v;
const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
const float3 co_2 = !vcos.is_empty() ? vcos[BM_elem_index_get(v_2)] : float3(v_2->co);
BLI_assert(v_1 == BM_edge_other_vert(l_curr->e, v_pivot));
BLI_assert(v_2 == BM_edge_other_vert(l_curr->prev->e, v_pivot));
@ -586,7 +592,8 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
int clnors_count = 0;
bool clnors_invalid = false;
const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
const float3 co_pivot = !vcos.is_empty() ? vcos[BM_elem_index_get(v_pivot)] :
float3(v_pivot->co);
MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) :
nullptr;
@ -601,7 +608,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
* then we can just reuse old current one! */
{
const BMVert *v_2 = lfan_pivot->next->v;
const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
const float3 co_2 = !vcos.is_empty() ? vcos[BM_elem_index_get(v_2)] : float3(v_2->co);
BLI_assert(v_2 == BM_edge_other_vert(e_next, v_pivot));
@ -633,7 +640,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
*/
{
const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
const float3 co_2 = !vcos.is_empty() ? vcos[BM_elem_index_get(v_2)] : float3(v_2->co);
sub_v3_v3v3(vec_next, co_2, co_pivot);
normalize_v3(vec_next);
@ -644,7 +651,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
/* Calculate angle between the two face edges incident on this vertex. */
const BMFace *f = lfan_pivot->f;
const float fac = blender::math::safe_acos_approx(dot_v3v3(vec_next, vec_curr));
const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
const float3 no = !fnos.is_empty() ? fnos[BM_elem_index_get(f)] : float3(f->no);
/* Accumulate */
madd_v3_v3fl(lnor, no, fac);
@ -795,16 +802,18 @@ BLI_INLINE bool bm_edge_is_smooth_no_angle_test(const BMEdge *e,
BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH));
}
static void bm_edge_tag_from_smooth(const float (*fnos)[3], BMEdge *e, const float split_angle_cos)
static void bm_edge_tag_from_smooth(const Span<float3> fnos,
BMEdge *e,
const float split_angle_cos)
{
BLI_assert(e->l != nullptr);
BMLoop *l_a = e->l, *l_b = l_a->radial_next;
bool is_smooth = false;
if (bm_edge_is_smooth_no_angle_test(e, l_a, l_b)) {
if (split_angle_cos != -1.0f) {
const float dot = (fnos == nullptr) ? dot_v3v3(l_a->f->no, l_b->f->no) :
dot_v3v3(fnos[BM_elem_index_get(l_a->f)],
fnos[BM_elem_index_get(l_b->f)]);
const float dot = fnos.is_empty() ? dot_v3v3(l_a->f->no, l_b->f->no) :
dot_v3v3(fnos[BM_elem_index_get(l_a->f)],
fnos[BM_elem_index_get(l_b->f)]);
if (dot >= split_angle_cos) {
is_smooth = true;
}
@ -834,7 +843,7 @@ static void bm_edge_tag_from_smooth(const float (*fnos)[3], BMEdge *e, const flo
* \note This doesn't have the same atomic requirement as #bm_edge_tag_from_smooth
* since it isn't run from multiple threads at once.
*/
static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3],
static void bm_edge_tag_from_smooth_and_set_sharp(const Span<float3> fnos,
BMEdge *e,
const float split_angle_cos)
{
@ -843,9 +852,9 @@ static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3],
bool is_smooth = false;
if (bm_edge_is_smooth_no_angle_test(e, l_a, l_b)) {
if (split_angle_cos != -1.0f) {
const float dot = (fnos == nullptr) ? dot_v3v3(l_a->f->no, l_b->f->no) :
dot_v3v3(fnos[BM_elem_index_get(l_a->f)],
fnos[BM_elem_index_get(l_b->f)]);
const float dot = fnos.is_empty() ? dot_v3v3(l_a->f->no, l_b->f->no) :
dot_v3v3(fnos[BM_elem_index_get(l_a->f)],
fnos[BM_elem_index_get(l_b->f)]);
if (dot >= split_angle_cos) {
is_smooth = true;
}
@ -871,9 +880,9 @@ static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3],
*/
static void bm_mesh_loops_calc_normals_for_vert_with_clnors(
BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
float (*r_lnos)[3],
const Span<float3> vcos,
const Span<float3> fnos,
MutableSpan<float3> r_lnos,
const short (*clnors_data)[2],
const int cd_loop_clnors_offset,
const bool do_rebuild,
@ -993,9 +1002,9 @@ static void bm_mesh_loops_calc_normals_for_vert_with_clnors(
*/
static void bm_mesh_loops_calc_normals_for_vert_without_clnors(
BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
float (*r_lnos)[3],
const Span<float3> vcos,
const Span<float3> fnos,
MutableSpan<float3> r_lnos,
const bool do_rebuild,
const float split_angle_cos,
/* TLS */
@ -1070,9 +1079,9 @@ static void bm_mesh_loops_calc_normals_for_vert_without_clnors(
* we could add a low-level API flag for this, see #BM_ELEM_API_FLAG_ENABLE and friends.
*/
static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
float (*r_lnos)[3],
const Span<float3> vcos,
const Span<float3> fnos,
MutableSpan<float3> r_lnos,
MLoopNorSpaceArray *r_lnors_spacearr,
const short (*clnors_data)[2],
const int cd_loop_clnors_offset,
@ -1091,7 +1100,7 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
{
char htype = 0;
if (vcos) {
if (!vcos.is_empty()) {
htype |= BM_VERT;
}
/* Face/Loop indices are set inline below. */
@ -1164,8 +1173,8 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
struct BMLoopsCalcNormalsWithCoordsData {
/* Read-only data. */
const float (*fnos)[3];
const float (*vcos)[3];
Span<float3> vcos;
Span<float3> fnos;
BMesh *bm;
const short (*clnors_data)[2];
int cd_loop_clnors_offset;
@ -1173,7 +1182,7 @@ struct BMLoopsCalcNormalsWithCoordsData {
float split_angle_cos;
/* Output. */
float (*r_lnos)[3];
MutableSpan<float3> r_lnos;
MLoopNorSpaceArray *r_lnors_spacearr;
};
@ -1272,9 +1281,9 @@ static void bm_mesh_loops_calc_normals_for_vert_without_clnors_fn(
}
static void bm_mesh_loops_calc_normals__multi_threaded(BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
float (*r_lnos)[3],
const Span<float3> vcos,
const Span<float3> fnos,
MutableSpan<float3> r_lnos,
MLoopNorSpaceArray *r_lnors_spacearr,
const short (*clnors_data)[2],
const int cd_loop_clnors_offset,
@ -1286,10 +1295,10 @@ static void bm_mesh_loops_calc_normals__multi_threaded(BMesh *bm,
{
char htype = BM_LOOP;
if (vcos) {
if (!vcos.is_empty()) {
htype |= BM_VERT;
}
if (fnos) {
if (!fnos.is_empty()) {
htype |= BM_FACE;
}
/* Face/Loop indices are set inline below. */
@ -1347,9 +1356,9 @@ static void bm_mesh_loops_calc_normals__multi_threaded(BMesh *bm,
}
static void bm_mesh_loops_calc_normals(BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
float (*r_lnos)[3],
const Span<float3> vcos,
const Span<float3> fnos,
MutableSpan<float3> r_lnos,
MLoopNorSpaceArray *r_lnors_spacearr,
const short (*clnors_data)[2],
const int cd_loop_clnors_offset,
@ -1572,8 +1581,8 @@ static void bm_mesh_loops_assign_normal_data(BMesh *bm,
* instead, depending on the do_split_fans parameter.
*/
static void bm_mesh_loops_custom_normals_set(BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
const Span<float3> vcos,
const Span<float3> fnos,
MLoopNorSpaceArray *r_lnors_spacearr,
short (*r_clnors_data)[2],
const int cd_loop_clnors_offset,
@ -1584,8 +1593,7 @@ static void bm_mesh_loops_custom_normals_set(BMesh *bm,
BMFace *f;
BMLoop *l;
BMIter liter, fiter;
float(*cur_lnors)[3] = static_cast<float(*)[3]>(
MEM_mallocN(sizeof(*cur_lnors) * bm->totloop, __func__));
Array<float3> cur_lnors(bm->totloop);
BKE_lnor_spacearr_clear(r_lnors_spacearr);
@ -1653,27 +1661,25 @@ static void bm_mesh_loops_custom_normals_set(BMesh *bm,
bm_mesh_loops_assign_normal_data(
bm, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, custom_lnors);
MEM_freeN(cur_lnors);
if (custom_lnors != new_lnors) {
MEM_freeN(custom_lnors);
}
}
static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm,
const float (*vnos)[3],
const float (*fnos)[3],
float (*r_lnos)[3])
const Span<float3> vnos,
const Span<float3> fnos,
MutableSpan<float3> r_lnos)
{
BMIter fiter;
BMFace *f_curr;
{
char htype = BM_LOOP;
if (vnos) {
if (!vnos.is_empty()) {
htype |= BM_VERT;
}
if (fnos) {
if (!fnos.is_empty()) {
htype |= BM_FACE;
}
BM_mesh_elem_index_ensure(bm, htype);
@ -1685,8 +1691,10 @@ static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm,
l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
do {
const float *no = is_face_flat ? (fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no) :
(vnos ? vnos[BM_elem_index_get(l_curr->v)] : l_curr->v->no);
const float3 no = is_face_flat ? (!fnos.is_empty() ? fnos[BM_elem_index_get(f_curr)] :
float3(f_curr->no)) :
(!vnos.is_empty() ? vnos[BM_elem_index_get(l_curr->v)] :
float3(l_curr->v->no));
copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no);
} while ((l_curr = l_curr->next) != l_first);
@ -1694,11 +1702,11 @@ static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm,
}
void BM_loops_calc_normal_vcos(BMesh *bm,
const float (*vcos)[3],
const float (*vnos)[3],
const float (*fnos)[3],
const Span<float3> vcos,
const Span<float3> vnos,
const Span<float3> fnos,
const bool use_split_normals,
float (*r_lnos)[3],
MutableSpan<float3> r_lnos,
MLoopNorSpaceArray *r_lnors_spacearr,
short (*clnors_data)[2],
const int cd_loop_clnors_offset,
@ -1728,7 +1736,7 @@ void BM_loops_calc_normal_vcos(BMesh *bm,
/** \name Loop Normal Space API
* \{ */
void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3])
void BM_lnorspacearr_store(BMesh *bm, MutableSpan<float3> r_lnors)
{
BLI_assert(bm->lnor_spacearr != nullptr);
@ -1738,16 +1746,8 @@ void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3])
int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
BM_loops_calc_normal_vcos(bm,
nullptr,
nullptr,
nullptr,
true,
r_lnors,
bm->lnor_spacearr,
nullptr,
cd_loop_clnors_offset,
false);
BM_loops_calc_normal_vcos(
bm, {}, {}, {}, true, r_lnors, bm->lnor_spacearr, nullptr, cd_loop_clnors_offset, false);
bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
}
@ -1834,10 +1834,8 @@ void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
BMLoop *l;
BMIter fiter, liter;
float(*r_lnors)[3] = static_cast<float(*)[3]>(
MEM_callocN(sizeof(*r_lnors) * bm->totloop, __func__));
float(*oldnors)[3] = static_cast<float(*)[3]>(
preserve_clnor ? MEM_mallocN(sizeof(*oldnors) * bm->totloop, __func__) : nullptr);
Array<float3> r_lnors(bm->totloop, float3(0));
Array<float3> oldnors(preserve_clnor ? bm->totloop : 0, float3(0));
int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
@ -1865,17 +1863,8 @@ void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
BKE_lnor_spacearr_clear(bm->lnor_spacearr);
}
BM_loops_calc_normal_vcos(bm,
nullptr,
nullptr,
nullptr,
true,
r_lnors,
bm->lnor_spacearr,
nullptr,
cd_loop_clnors_offset,
true);
MEM_freeN(r_lnors);
BM_loops_calc_normal_vcos(
bm, {}, {}, {}, true, r_lnors, bm->lnor_spacearr, nullptr, cd_loop_clnors_offset, true);
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
@ -1894,7 +1883,6 @@ void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
}
}
MEM_SAFE_FREE(oldnors);
bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
#ifndef NDEBUG
@ -1908,12 +1896,8 @@ void BM_lnorspace_update(BMesh *bm)
bm->lnor_spacearr = MEM_cnew<MLoopNorSpaceArray>(__func__);
}
if (bm->lnor_spacearr->lspacearr == nullptr) {
float(*lnors)[3] = static_cast<float(*)[3]>(
MEM_callocN(sizeof(*lnors) * bm->totloop, __func__));
Array<float3> lnors(bm->totloop, float3(0));
BM_lnorspacearr_store(bm, lnors);
MEM_freeN(lnors);
}
else if (bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)) {
BM_lnorspace_rebuild(bm, false);
@ -1945,9 +1929,9 @@ void BM_lnorspace_err(BMesh *bm)
BKE_lnor_spacearr_init(temp, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
float(*lnors)[3] = static_cast<float(*)[3]>(MEM_callocN(sizeof(*lnors) * bm->totloop, __func__));
Array<float3> lnors(bm->totloop, float3(0));
BM_loops_calc_normal_vcos(
bm, nullptr, nullptr, nullptr, true, lnors, temp, nullptr, cd_loop_clnors_offset, true);
bm, {}, {}, {}, true, lnors, temp, nullptr, cd_loop_clnors_offset, true);
for (int i = 0; i < bm->totloop; i++) {
int j = 0;
@ -1969,7 +1953,6 @@ void BM_lnorspace_err(BMesh *bm)
}
BKE_lnor_spacearr_free(temp);
MEM_freeN(temp);
MEM_freeN(lnors);
BLI_assert(clear);
bm->spacearr_dirty &= ~BM_SPACEARR_DIRTY_ALL;
@ -2268,8 +2251,8 @@ void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
}
bm_mesh_loops_custom_normals_set(bm,
nullptr,
nullptr,
{},
{},
bm->lnor_spacearr,
nullptr,
cd_custom_normal_offset,

View File

@ -43,9 +43,9 @@ void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpin
* using given vertex coordinates (vcos) and polygon normals (fnos).
*/
void BM_verts_calc_normal_vcos(BMesh *bm,
const float (*fnos)[3],
const float (*vcos)[3],
float (*vnos)[3]);
blender::Span<blender::float3> fnos,
blender::Span<blender::float3> vcos,
blender::MutableSpan<blender::float3> vnos);
/**
* \brief BMesh Compute Loop Normals from/to external data.
*
@ -54,11 +54,11 @@ void BM_verts_calc_normal_vcos(BMesh *bm,
* (splitting edges).
*/
void BM_loops_calc_normal_vcos(BMesh *bm,
const float (*vcos)[3],
const float (*vnos)[3],
const float (*fnos)[3],
blender::Span<blender::float3> vcos,
blender::Span<blender::float3> vnos,
blender::Span<blender::float3> fnos,
bool use_split_normals,
float (*r_lnos)[3],
blender::MutableSpan<blender::float3> r_lnos,
MLoopNorSpaceArray *r_lnors_spacearr,
short (*clnors_data)[2],
int cd_loop_clnors_offset,
@ -70,7 +70,7 @@ void BM_loops_calc_normal_vcos(BMesh *bm,
* and yet we need to walk them once, and only once.
*/
bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr);
void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3]);
void BM_lnorspacearr_store(BMesh *bm, blender::MutableSpan<blender::float3> r_lnors);
void BM_lnorspace_invalidate(BMesh *bm, bool do_invalidate_all);
void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor);
/**

View File

@ -31,6 +31,9 @@
#include "intern/bmesh_private.hh"
using blender::float3;
using blender::Span;
/**
* \brief COMPUTE POLY NORMAL (BMFace)
*
@ -66,7 +69,7 @@ static float bm_face_calc_poly_normal(const BMFace *f, float n[3])
*/
static float bm_face_calc_poly_normal_vertex_cos(const BMFace *f,
float r_no[3],
float const (*vertexCos)[3])
const Span<float3> vertexCos)
{
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
BMLoop *l_iter = l_first;
@ -742,7 +745,7 @@ void BM_face_normal_update(BMFace *f)
float BM_face_calc_normal_vcos(const BMesh *bm,
const BMFace *f,
float r_no[3],
float const (*vertexCos)[3])
const Span<float3> vertexCos)
{
BMLoop *l;

View File

@ -46,7 +46,7 @@ float BM_face_calc_normal(const BMFace *f, float r_no[3]) ATTR_NONNULL();
float BM_face_calc_normal_vcos(const BMesh *bm,
const BMFace *f,
float r_no[3],
float const (*vertexCos)[3]) ATTR_NONNULL();
blender::Span<blender::float3> vertexCos) ATTR_NONNULL();
/**
* Calculate a normal from a vertex cloud.

View File

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

View File

@ -49,7 +49,10 @@ class FileOutput {
public:
/* Allocate and initialize the internal render result of the file output using the give
* parameters. See the implementation for more information. */
FileOutput(std::string path, ImageFormatData format, int2 size, bool save_as_render);
FileOutput(const std::string &path,
const ImageFormatData &format,
int2 size,
bool save_as_render);
/* Free the internal render result. */
~FileOutput();

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