Merge branch 'master' into xr-dev

This commit is contained in:
Peter Kim 2022-02-23 16:48:02 +09:00
commit c664cc77af
107 changed files with 1522 additions and 1736 deletions

View File

@ -866,7 +866,7 @@ if(WITH_CYCLES_DEVICE_HIP)
endif()
#-----------------------------------------------------------------------------
# Check check if submodules are cloned
# Check if submodules are cloned.
if(WITH_INTERNATIONAL)
file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/datafiles/locale")
@ -1886,7 +1886,6 @@ elseif(WITH_CYCLES_STANDALONE)
add_subdirectory(intern/glew-mx)
add_subdirectory(intern/guardedalloc)
add_subdirectory(intern/libc_compat)
add_subdirectory(intern/numaapi)
add_subdirectory(intern/sky)
add_subdirectory(intern/cycles)

View File

@ -51,20 +51,13 @@ Testing Targets
* test:
Run automated tests with ctest.
* test_cmake:
Runs our own cmake file checker
which detects errors in the cmake file list definitions
* test_pep8:
Checks all python script are pep8
which are tagged to use the stricter formatting
* test_deprecated:
Checks for deprecation tags in our code which may need to be removed
Static Source Code Checking
Not associated with building Blender.
* check_cppcheck: Run blender source through cppcheck (C & C++).
* check_clang_array: Run blender source through clang array checking script (C & C++).
* check_deprecated: Check if there is any deprecated code to remove.
* check_splint: Run blenders source through splint (C only).
* check_sparse: Run blenders source through sparse (C only).
* check_smatch: Run blenders source through smatch (C only).
@ -73,6 +66,10 @@ Static Source Code Checking
using one of the accepted licenses in 'doc/license/SPDX-license-identifiers.txt'
Append with 'SHOW_HEADERS=1' to show all unique headers
which can be useful for spotting license irregularities.
* check_cmake: Runs our own cmake file checker which detects errors in the cmake file list definitions.
* check_pep8: Checks all Python script are pep8 which are tagged to use the stricter formatting.
* check_mypy: Checks all Python scripts using mypy,
see: source/tools/check_source/check_mypy_config.py scripts which are included.
Spell Checkers
This runs the spell checker from the developer tools repositor.
@ -400,20 +397,6 @@ package_archive: .FORCE
test: .FORCE
@$(PYTHON) ./build_files/utils/make_test.py "$(BUILD_DIR)"
# run pep8 check check on scripts we distribute.
test_pep8: .FORCE
@$(PYTHON) tests/python/pep8.py > test_pep8.log 2>&1
@echo "written: test_pep8.log"
# run some checks on our CMAKE files.
test_cmake: .FORCE
@$(PYTHON) build_files/cmake/cmake_consistency_check.py > test_cmake_consistency.log 2>&1
@echo "written: test_cmake_consistency.log"
# run deprecation tests, see if we have anything to remove.
test_deprecated: .FORCE
@$(PYTHON) tests/check_deprecated.py
# -----------------------------------------------------------------------------
# Project Files
@ -491,11 +474,23 @@ check_descriptions: .FORCE
@$(BLENDER_BIN) --background -noaudio --factory-startup --python \
"$(BLENDER_DIR)/source/tools/check_source/check_descriptions.py"
check_deprecated: .FORCE
@PYTHONIOENCODING=utf_8 $(PYTHON) \
source/tools/check_source/check_deprecated.py
check_licenses: .FORCE
@PYTHONIOENCODING=utf_8 $(PYTHON) \
"$(BLENDER_DIR)/source/tools/check_source/check_licenses.py" \
"--show-headers=$(SHOW_HEADERS)"
check_pep8: .FORCE
@PYTHONIOENCODING=utf_8 $(PYTHON) \
tests/python/pep8.py
check_cmake: .FORCE
@PYTHONIOENCODING=utf_8 $(PYTHON) \
source/tools/check_source/check_cmake_consistency.py
# -----------------------------------------------------------------------------
# Utilities

View File

@ -1,387 +0,0 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
# <pep8 compliant>
# Note: this code should be cleaned up / refactored.
import sys
if sys.version_info.major < 3:
print("\nPython3.x needed, found %s.\nAborting!\n" %
sys.version.partition(" ")[0])
sys.exit(1)
import os
from os.path import (
dirname,
join,
normpath,
splitext,
)
from cmake_consistency_check_config import (
IGNORE_SOURCE,
IGNORE_SOURCE_MISSING,
IGNORE_CMAKE,
UTF8_CHECK,
SOURCE_DIR,
BUILD_DIR,
)
from typing import (
Callable,
Dict,
Generator,
Iterator,
List,
Optional,
Tuple,
)
global_h = set()
global_c = set()
global_refs: Dict[str, List[Tuple[str, int]]] = {}
# Flatten `IGNORE_SOURCE_MISSING` to avoid nested looping.
IGNORE_SOURCE_MISSING_FLAT = [
(k, ignore_path) for k, ig_list in IGNORE_SOURCE_MISSING
for ignore_path in ig_list
]
# Ignore cmake file, path pairs.
global_ignore_source_missing: Dict[str, List[str]] = {}
for k, v in IGNORE_SOURCE_MISSING_FLAT:
global_ignore_source_missing.setdefault(k, []).append(v)
del IGNORE_SOURCE_MISSING_FLAT
def replace_line(f: str, i: int, text: str, keep_indent: bool = True) -> None:
file_handle = open(f, 'r')
data = file_handle.readlines()
file_handle.close()
l = data[i]
ws = l[:len(l) - len(l.lstrip())]
data[i] = "%s%s\n" % (ws, text)
file_handle = open(f, 'w')
file_handle.writelines(data)
file_handle.close()
def source_list(
path: str,
filename_check: Optional[Callable[[str], bool]] = None,
) -> Generator[str, None, None]:
for dirpath, dirnames, filenames in os.walk(path):
# skip '.git'
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
for filename in filenames:
if filename_check is None or filename_check(filename):
yield os.path.join(dirpath, filename)
# extension checking
def is_cmake(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext == ".cmake") or (filename == "CMakeLists.txt")
def is_c_header(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext in {".h", ".hpp", ".hxx", ".hh"})
def is_c(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".metal"})
def is_c_any(filename: str) -> bool:
return is_c(filename) or is_c_header(filename)
def cmake_get_src(f: str) -> None:
sources_h = []
sources_c = []
filen = open(f, "r", encoding="utf8")
it: Optional[Iterator[str]] = iter(filen)
found = False
i = 0
# print(f)
def is_definition(l: str, f: str, i: int, name: str) -> bool:
if l.startswith("unset("):
return False
if ('set(%s' % name) in l or ('set(' in l and l.endswith(name)):
if len(l.split()) > 1:
raise Exception("strict formatting not kept 'set(%s*' %s:%d" % (name, f, i))
return True
if ("list(APPEND %s" % name) in l or ('list(APPEND ' in l and l.endswith(name)):
if l.endswith(")"):
raise Exception("strict formatting not kept 'list(APPEND %s...)' on 1 line %s:%d" % (name, f, i))
return True
return False
while it is not None:
context_name = ""
while it is not None:
i += 1
try:
l = next(it)
except StopIteration:
it = None
break
l = l.strip()
if not l.startswith("#"):
found = is_definition(l, f, i, "SRC")
if found:
context_name = "SRC"
break
found = is_definition(l, f, i, "INC")
if found:
context_name = "INC"
break
if found:
cmake_base = dirname(f)
cmake_base_bin = os.path.join(BUILD_DIR, os.path.relpath(cmake_base, SOURCE_DIR))
# Find known missing sources list (if we have one).
f_rel = os.path.relpath(f, SOURCE_DIR)
f_rel_key = f_rel
if os.sep != "/":
f_rel_key = f_rel_key.replace(os.sep, "/")
local_ignore_source_missing = global_ignore_source_missing.get(f_rel_key, [])
while it is not None:
i += 1
try:
l = next(it)
except StopIteration:
it = None
break
l = l.strip()
if not l.startswith("#"):
# Remove in-line comments.
l = l.split(" # ")[0].rstrip()
if ")" in l:
if l.strip() != ")":
raise Exception("strict formatting not kept '*)' %s:%d" % (f, i))
break
# replace dirs
l = l.replace("${CMAKE_SOURCE_DIR}", SOURCE_DIR)
l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base)
l = l.replace("${CMAKE_CURRENT_BINARY_DIR}", cmake_base_bin)
l = l.strip('"')
if not l:
pass
elif l in local_ignore_source_missing:
local_ignore_source_missing.remove(l)
elif l.startswith("$"):
if context_name == "SRC":
# assume if it ends with context_name we know about it
if not l.split("}")[0].endswith(context_name):
print("Can't use var '%s' %s:%d" % (l, f, i))
elif len(l.split()) > 1:
raise Exception("Multi-line define '%s' %s:%d" % (l, f, i))
else:
new_file = normpath(join(cmake_base, l))
if context_name == "SRC":
if is_c_header(new_file):
sources_h.append(new_file)
global_refs.setdefault(new_file, []).append((f, i))
elif is_c(new_file):
sources_c.append(new_file)
global_refs.setdefault(new_file, []).append((f, i))
elif l in {"PARENT_SCOPE", }:
# cmake var, ignore
pass
elif new_file.endswith(".list"):
pass
elif new_file.endswith(".def"):
pass
elif new_file.endswith(".cl"): # opencl
pass
elif new_file.endswith(".cu"): # cuda
pass
elif new_file.endswith(".osl"): # open shading language
pass
elif new_file.endswith(".glsl"):
pass
else:
raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file))
elif context_name == "INC":
if new_file.startswith(BUILD_DIR):
# assume generated path
pass
elif os.path.isdir(new_file):
new_path_rel = os.path.relpath(new_file, cmake_base)
if new_path_rel != l:
print("overly relative path:\n %s:%d\n %s\n %s" % (f, i, l, new_path_rel))
# # Save time. just replace the line
# replace_line(f, i - 1, new_path_rel)
else:
raise Exception("non existent include %s:%d -> %s" % (f, i, new_file))
# print(new_file)
global_h.update(set(sources_h))
global_c.update(set(sources_c))
'''
if not sources_h and not sources_c:
raise Exception("No sources %s" % f)
sources_h_fs = list(source_list(cmake_base, is_c_header))
sources_c_fs = list(source_list(cmake_base, is_c))
'''
# find missing C files:
'''
for ff in sources_c_fs:
if ff not in sources_c:
print(" missing: " + ff)
'''
# reset
del sources_h[:]
del sources_c[:]
filen.close()
def is_ignore_source(f: str, ignore_used: List[bool]) -> bool:
for index, ignore_path in enumerate(IGNORE_SOURCE):
if ignore_path in f:
ignore_used[index] = True
return True
return False
def is_ignore_cmake(f: str, ignore_used: List[bool]) -> bool:
for index, ignore_path in enumerate(IGNORE_CMAKE):
if ignore_path in f:
ignore_used[index] = True
return True
return False
def main() -> None:
print("Scanning:", SOURCE_DIR)
ignore_used_source = [False] * len(IGNORE_SOURCE)
ignore_used_cmake = [False] * len(IGNORE_CMAKE)
for cmake in source_list(SOURCE_DIR, is_cmake):
if not is_ignore_cmake(cmake, ignore_used_cmake):
cmake_get_src(cmake)
# First do stupid check, do these files exist?
print("\nChecking for missing references:")
is_err = False
errs = []
for f in (global_h | global_c):
if f.startswith(BUILD_DIR):
continue
if not os.path.exists(f):
refs = global_refs[f]
if refs:
for cf, i in refs:
errs.append((cf, i))
else:
raise Exception("CMake references missing, internal error, aborting!")
is_err = True
errs.sort()
errs.reverse()
for cf, i in errs:
print("%s:%d" % (cf, i))
# Write a 'sed' script, useful if we get a lot of these
# print("sed '%dd' '%s' > '%s.tmp' ; mv '%s.tmp' '%s'" % (i, cf, cf, cf, cf))
if is_err:
raise Exception("CMake references missing files, aborting!")
del is_err
del errs
# now check on files not accounted for.
print("\nC/C++ Files CMake does not know about...")
for cf in sorted(source_list(SOURCE_DIR, is_c)):
if not is_ignore_source(cf, ignore_used_source):
if cf not in global_c:
print("missing_c: ", cf)
# Check if automake builds a corresponding .o file.
'''
if cf in global_c:
out1 = os.path.splitext(cf)[0] + ".o"
out2 = os.path.splitext(cf)[0] + ".Po"
out2_dir, out2_file = out2 = os.path.split(out2)
out2 = os.path.join(out2_dir, ".deps", out2_file)
if not os.path.exists(out1) and not os.path.exists(out2):
print("bad_c: ", cf)
'''
print("\nC/C++ Headers CMake does not know about...")
for hf in sorted(source_list(SOURCE_DIR, is_c_header)):
if not is_ignore_source(hf, ignore_used_source):
if hf not in global_h:
print("missing_h: ", hf)
if UTF8_CHECK:
# test encoding
import traceback
for files in (global_c, global_h):
for f in sorted(files):
if os.path.exists(f):
# ignore outside of our source tree
if "extern" not in f:
i = 1
try:
for _ in open(f, "r", encoding="utf8"):
i += 1
except UnicodeDecodeError:
print("Non utf8: %s:%d" % (f, i))
if i > 1:
traceback.print_exc()
# Check ignores aren't stale
print("\nCheck for unused 'IGNORE_SOURCE' paths...")
for index, ignore_path in enumerate(IGNORE_SOURCE):
if not ignore_used_source[index]:
print("unused ignore: %r" % ignore_path)
# Check ignores aren't stale
print("\nCheck for unused 'IGNORE_SOURCE_MISSING' paths...")
for k, v in sorted(global_ignore_source_missing.items()):
for ignore_path in v:
print("unused ignore: %r -> %r" % (ignore_path, k))
# Check ignores aren't stale
print("\nCheck for unused 'IGNORE_CMAKE' paths...")
for index, ignore_path in enumerate(IGNORE_CMAKE):
if not ignore_used_cmake[index]:
print("unused ignore: %r" % ignore_path)
if __name__ == "__main__":
main()

View File

@ -1,64 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import os
IGNORE_SOURCE = (
"/test/",
"/tests/gtests/",
"/release/",
# specific source files
"extern/audaspace/",
# Use for `WIN32` only.
"source/creator/blender_launcher_win32.c",
# specific source files
"extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp",
"extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp",
"extern/bullet2/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp",
"extern/bullet2/src/BulletCollision/CollisionShapes/btBox2dShape.cpp",
"extern/bullet2/src/BulletCollision/CollisionShapes/btConvex2dShape.cpp",
"extern/bullet2/src/BulletDynamics/Character/btKinematicCharacterController.cpp",
"extern/bullet2/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp",
"extern/bullet2/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp",
"doc/doxygen/doxygen.extern.h",
"doc/doxygen/doxygen.intern.h",
"doc/doxygen/doxygen.main.h",
"doc/doxygen/doxygen.source.h",
"extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h",
"extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.h",
"extern/bullet2/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.h",
"extern/bullet2/src/BulletCollision/CollisionShapes/btBox2dShape.h",
"extern/bullet2/src/BulletCollision/CollisionShapes/btConvex2dShape.h",
"extern/bullet2/src/BulletDynamics/Character/btKinematicCharacterController.h",
"extern/bullet2/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.h",
"extern/bullet2/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.h",
)
# Ignore cmake file, path pairs.
IGNORE_SOURCE_MISSING = (
( # Use for cycles stand-alone.
"intern/cycles/util/CMakeLists.txt", (
"../../third_party/numaapi/include",
)),
( # Use for `WITH_NANOVDB`.
"intern/cycles/kernel/CMakeLists.txt", (
"nanovdb/util/CSampleFromVoxels.h",
"nanovdb/util/SampleFromVoxels.h",
"nanovdb/NanoVDB.h",
"nanovdb/CNanoVDB.h",
),
),
)
IGNORE_CMAKE = (
"extern/audaspace/CMakeLists.txt",
)
UTF8_CHECK = True
SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", ".."))))
# doesn't have to exist, just use as reference
BUILD_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(SOURCE_DIR, "..", "build"))))

View File

@ -849,3 +849,45 @@ if(WITH_COMPILER_CCACHE)
set(WITH_COMPILER_CCACHE OFF)
endif()
endif()
# On some platforms certain atomic operations are not possible with assembly and/or intrinsics and
# they are emulated in software with locks. For example, on armel there is no intrinsics to grant
# 64 bit atomic operations and STL library uses libatomic to offload software emulation of atomics
# to.
# This function will check whether libatomic is required and if so will configure linker flags.
# If atomic operations are possible without libatomic then linker flags are left as-is.
function(CONFIGURE_ATOMIC_LIB_IF_NEEDED)
# Source which is used to enforce situation when software emulation of atomics is required.
# Assume that using 64bit integer gives a definitive asnwer (as in, if 64bit atomic operations
# are possible using assembly/intrinsics 8, 16, and 32 bit operations will also be possible.
set(_source
"#include <atomic>
#include <cstdint>
int main(int argc, char **argv) {
std::atomic<uint64_t> uint64; uint64++;
return 0;
}")
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITHOUT_LIBATOMIC)
if(NOT ATOMIC_OPS_WITHOUT_LIBATOMIC)
# Compilation of the test program has failed.
# Try it again with -latomic to see if this is what is needed, or whether something else is
# going on.
set(CMAKE_REQUIRED_LIBRARIES atomic)
check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITH_LIBATOMIC)
if(ATOMIC_OPS_WITH_LIBATOMIC)
set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -latomic" PARENT_SCOPE)
else()
# Atomic operations are required part of Blender and it is not possible to process forward.
# We expect that either standard library or libatomic will make atomics to work. If both
# cases has failed something fishy o na bigger scope is going on.
message(FATAL_ERROR "Failed to detect required configuration for atomic operations")
endif()
endif()
endfunction()
CONFIGURE_ATOMIC_LIB_IF_NEEDED()

View File

@ -1,5 +1,6 @@
/* Override RTD theme */
.rst-versions {
display: none;
border-top: 0px;
overflow: visible;
}

View File

@ -34,8 +34,14 @@ endif()
if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
add_definitions(${GL_DEFINITIONS})
list(APPEND INC_SYS ${GLEW_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS})
list(APPEND LIBRARIES ${CYCLES_GL_LIBRARIES} ${SDL2_LIBRARIES})
list(APPEND INC_SYS
${GLEW_INCLUDE_DIR}
${SDL2_INCLUDE_DIRS}
)
list(APPEND LIBRARIES
${CYCLES_GL_LIBRARIES}
${SDL2_LIBRARIES}
)
endif()
# Common configuration.

View File

@ -75,7 +75,7 @@ bool OpenGLDisplayDriver::update_begin(const Params &params, int texture_width,
/* Update PBO dimensions if needed.
*
* NOTE: Allocate the PBO for the the size which will fit the final render resolution (as in,
* NOTE: Allocate the PBO for the size which will fit the final render resolution (as in,
* at a resolution divider 1. This was we don't need to recreate graphics interoperability
* objects which are costly and which are tied to the specific underlying buffer size.
* The downside of this approach is that when graphics interoperability is not used we are

View File

@ -191,7 +191,7 @@ device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_
void CPUDevice::const_copy_to(const char *name, void *host, size_t size)
{
#if WITH_EMBREE
#ifdef WITH_EMBREE
if (strcmp(name, "__data") == 0) {
assert(size <= sizeof(KernelData));

View File

@ -244,7 +244,7 @@ void RenderScheduler::render_work_reschedule_on_cancel(RenderWork &render_work)
render_work.tile.write = tile_write;
render_work.full.write = full_write;
/* Do not write tile if it has zero samples it it, treat it similarly to all other tiles which
/* Do not write tile if it has zero samples it, treat it similarly to all other tiles which
* got canceled. */
if (!state_.tile_result_was_written && has_rendered_samples) {
render_work.tile.write = true;

View File

@ -124,7 +124,7 @@ class RenderScheduler {
/* Get sample up to which rendering has been done.
* This is an absolute 0-based value.
*
* For example, if start sample is 10 and and 5 samples were rendered, then this call will
* For example, if start sample is 10 and 5 samples were rendered, then this call will
* return 14.
*
* If there were no samples rendered, then the behavior is undefined. */
@ -132,7 +132,7 @@ class RenderScheduler {
/* Get number of samples rendered within the current scheduling session.
*
* For example, if start sample is 10 and and 5 samples were rendered, then this call will
* For example, if start sample is 10 and 5 samples were rendered, then this call will
* return 5.
*
* Note that this is based on the scheduling information. In practice this means that if someone

View File

@ -4,12 +4,13 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/log.h>
}
namespace {
bool test_vcodec(AVCodec *codec, AVPixelFormat pixelformat)
bool test_vcodec(const AVCodec *codec, AVPixelFormat pixelformat)
{
av_log_set_level(AV_LOG_QUIET);
bool result = false;
@ -30,7 +31,7 @@ bool test_vcodec(AVCodec *codec, AVPixelFormat pixelformat)
}
return result;
}
bool test_acodec(AVCodec *codec, AVSampleFormat fmt)
bool test_acodec(const AVCodec *codec, AVSampleFormat fmt)
{
av_log_set_level(AV_LOG_QUIET);
bool result = false;
@ -54,7 +55,7 @@ bool test_acodec(AVCodec *codec, AVSampleFormat fmt)
bool test_codec_video_by_codecid(AVCodecID codec_id, AVPixelFormat pixelformat)
{
bool result = false;
AVCodec *codec = avcodec_find_encoder(codec_id);
const AVCodec *codec = avcodec_find_encoder(codec_id);
if (codec)
result = test_vcodec(codec, pixelformat);
return result;
@ -63,7 +64,7 @@ bool test_codec_video_by_codecid(AVCodecID codec_id, AVPixelFormat pixelformat)
bool test_codec_video_by_name(const char *codecname, AVPixelFormat pixelformat)
{
bool result = false;
AVCodec *codec = avcodec_find_encoder_by_name(codecname);
const AVCodec *codec = avcodec_find_encoder_by_name(codecname);
if (codec)
result = test_vcodec(codec, pixelformat);
return result;
@ -72,7 +73,7 @@ bool test_codec_video_by_name(const char *codecname, AVPixelFormat pixelformat)
bool test_codec_audio_by_codecid(AVCodecID codec_id, AVSampleFormat fmt)
{
bool result = false;
AVCodec *codec = avcodec_find_encoder(codec_id);
const AVCodec *codec = avcodec_find_encoder(codec_id);
if (codec)
result = test_acodec(codec, fmt);
return result;
@ -81,7 +82,7 @@ bool test_codec_audio_by_codecid(AVCodecID codec_id, AVSampleFormat fmt)
bool test_codec_audio_by_name(const char *codecname, AVSampleFormat fmt)
{
bool result = false;
AVCodec *codec = avcodec_find_encoder_by_name(codecname);
const AVCodec *codec = avcodec_find_encoder_by_name(codecname);
if (codec)
result = test_acodec(codec, fmt);
return result;

View File

@ -506,11 +506,11 @@ static void updateGPUDisplayParameters(OCIO_GPUShader &shader,
data.dither = dither;
do_update = true;
}
if (data.use_predivide != use_predivide) {
if (bool(data.use_predivide) != use_predivide) {
data.use_predivide = use_predivide;
do_update = true;
}
if (data.use_overlay != use_overlay) {
if (bool(data.use_overlay) != use_overlay) {
data.use_overlay = use_overlay;
do_update = true;
}

View File

@ -1038,9 +1038,6 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False):
if props is None:
print("Error:", gizmo_group, "could not access properties!")
else:
for key in props.bl_rna.properties.keys():
props.property_unset(key)
gizmo_properties = item.widget_properties
if gizmo_properties is not None:
if not isinstance(gizmo_properties, list):

View File

@ -227,7 +227,7 @@ struct WriteAttributeLookup {
* - An output attribute can live side by side with an existing attribute with a different domain
* or data type. The old attribute will only be overwritten when the #save function is called.
*
* \note The lifetime of an output attribute should not be longer than the the lifetime of the
* \note The lifetime of an output attribute should not be longer than the lifetime of the
* geometry component it comes from, since it can keep a reference to the component for use in
* the #save method.
*/

View File

@ -693,7 +693,7 @@ class CurveComponent : public GeometryComponent {
/**
* Holds a reference to conceptually unique geometry or a pointer to object/collection data
* that is is instanced with a transform in #InstancesComponent.
* that is instanced with a transform in #InstancesComponent.
*/
class InstanceReference {
public:

View File

@ -169,7 +169,7 @@ struct IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop,
/*-------- Main Functions --------*/
/**
* Get the Group property that contains the id properties for ID id.
* Get the Group property that contains the id properties for ID `id`.
*
* \param create_if_needed: Set to create the group property and attach it to id if it doesn't
* exist; otherwise the function will return NULL if there's no Group property attached to the ID.

View File

@ -418,6 +418,17 @@ float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3];
*/
float (*BKE_mesh_poly_normals_for_write(struct Mesh *mesh))[3];
/**
* Free any cached vertex or poly normals. Face corner (loop) normals are also derived data,
* but are not handled with the same method yet, so they are not included. It's important that this
* is called after the mesh changes size, since otherwise cached normal arrays might not be large
* enough (though it may be called indirectly by other functions).
*
* \note Normally it's preferred to call #BKE_mesh_normals_tag_dirty instead,
* but this can be used in specific situations to reset a mesh or reduce memory usage.
*/
void BKE_mesh_clear_derived_normals(struct Mesh *mesh);
/**
* Mark the mesh's vertex normals non-dirty, for when they are calculated or assigned manually.
*/

View File

@ -10,6 +10,8 @@
#include "FN_generic_virtual_array.hh"
#include "DNA_curves_types.h"
#include "BLI_float4x4.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_vector.hh"
@ -49,12 +51,6 @@ using SplinePtr = std::unique_ptr<Spline>;
*/
class Spline {
public:
enum class Type {
Bezier,
NURBS,
Poly,
};
enum NormalCalculationMode {
ZUp,
Minimum,
@ -65,7 +61,7 @@ class Spline {
blender::bke::CustomDataAttributes attributes;
protected:
Type type_;
CurveType type_;
bool is_cyclic_ = false;
/** Direction of the spline at each evaluated point. */
@ -85,7 +81,7 @@ class Spline {
public:
virtual ~Spline() = default;
Spline(const Type type) : type_(type)
Spline(const CurveType type) : type_(type)
{
}
Spline(Spline &other) : attributes(other.attributes), type_(other.type_)
@ -107,7 +103,7 @@ class Spline {
SplinePtr copy_without_attributes() const;
static void copy_base_settings(const Spline &src, Spline &dst);
Spline::Type type() const;
CurveType type() const;
/** Return the number of control points. */
virtual int size() const = 0;
@ -252,26 +248,13 @@ class Spline {
* factors and indices in a list of floats, which is then used to interpolate any other data.
*/
class BezierSpline final : public Spline {
public:
enum class HandleType {
/** The handle can be moved anywhere, and doesn't influence the point's other handle. */
Free,
/** The location is automatically calculated to be smooth. */
Auto,
/** The location is calculated to point to the next/previous control point. */
Vector,
/** The location is constrained to point in the opposite direction as the other handle. */
Align,
};
private:
blender::Vector<blender::float3> positions_;
blender::Vector<float> radii_;
blender::Vector<float> tilts_;
int resolution_;
blender::Vector<HandleType> handle_types_left_;
blender::Vector<HandleType> handle_types_right_;
blender::Vector<int8_t> handle_types_left_;
blender::Vector<int8_t> handle_types_right_;
/* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
mutable blender::Vector<blender::float3> handle_positions_left_;
@ -296,7 +279,7 @@ class BezierSpline final : public Spline {
mutable bool mapping_cache_dirty_ = true;
public:
BezierSpline() : Spline(Type::Bezier)
BezierSpline() : Spline(CURVE_TYPE_BEZIER)
{
}
BezierSpline(const BezierSpline &other)
@ -323,8 +306,8 @@ class BezierSpline final : public Spline {
blender::Span<float> radii() const final;
blender::MutableSpan<float> tilts() final;
blender::Span<float> tilts() const final;
blender::Span<HandleType> handle_types_left() const;
blender::MutableSpan<HandleType> handle_types_left();
blender::Span<int8_t> handle_types_left() const;
blender::MutableSpan<int8_t> handle_types_left();
blender::Span<blender::float3> handle_positions_left() const;
/**
* Get writable access to the handle position.
@ -333,8 +316,8 @@ class BezierSpline final : public Spline {
* uninitialized memory while auto-generating handles.
*/
blender::MutableSpan<blender::float3> handle_positions_left(bool write_only = false);
blender::Span<HandleType> handle_types_right() const;
blender::MutableSpan<HandleType> handle_types_right();
blender::Span<int8_t> handle_types_right() const;
blender::MutableSpan<int8_t> handle_types_right();
blender::Span<blender::float3> handle_positions_right() const;
/**
* Get writable access to the handle position.
@ -519,7 +502,7 @@ class NURBSpline final : public Spline {
mutable bool position_cache_dirty_ = true;
public:
NURBSpline() : Spline(Type::NURBS)
NURBSpline() : Spline(CURVE_TYPE_NURBS)
{
}
NURBSpline(const NURBSpline &other)
@ -586,7 +569,7 @@ class PolySpline final : public Spline {
blender::Vector<float> tilts_;
public:
PolySpline() : Spline(Type::Poly)
PolySpline() : Spline(CURVE_TYPE_POLY)
{
}
PolySpline(const PolySpline &other)
@ -658,7 +641,7 @@ struct CurveEval {
* \note If you are looping over all of the splines in the same scope anyway,
* it's better to avoid calling this function, in case there are many splines.
*/
bool has_spline_with_type(const Spline::Type type) const;
bool has_spline_with_type(const CurveType type) const;
void resize(int size);
/**

View File

@ -377,6 +377,7 @@ set(SRC
BKE_idprop.hh
BKE_idtype.h
BKE_image.h
BKE_image_partial_update.hh
BKE_image_save.h
BKE_ipo.h
BKE_kelvinlet.h

View File

@ -52,7 +52,7 @@ TEST_F(AssetLibraryTest, bke_asset_library_load)
ASSERT_NE(nullptr, service);
/* Check that the catalogs defined in the library are actually loaded. This just tests one single
* catalog, as that indicates the file has been loaded. Testing that that loading went OK is for
* catalog, as that indicates the file has been loaded. Testing that loading went OK is for
* the asset catalog service tests. */
const bUUID uuid_poses_ellie("df60e1f6-2259-475b-93d9-69a1b4a8db78");
AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie);

View File

@ -144,7 +144,7 @@ static void brush_make_local(Main *bmain, ID *id, const int flags)
/* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided
* in IDType callbacks. Higher-level ID management code usually does not expect such things and
* does not deal properly with it. */
/* NOTE: assert below ensures that the comment above is valid, and that that exception is
/* NOTE: assert below ensures that the comment above is valid, and that exception is
* acceptable for the time being. */
BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0);
BLI_assert(!ID_IS_LINKED(brush->clone.image) && brush->clone.image->id.newid == NULL);

View File

@ -36,7 +36,7 @@ blender::MutableSpan<SplinePtr> CurveEval::splines()
return splines_;
}
bool CurveEval::has_spline_with_type(const Spline::Type type) const
bool CurveEval::has_spline_with_type(const CurveType type) const
{
for (const SplinePtr &spline : this->splines()) {
if (spline->type() == type) {
@ -160,24 +160,24 @@ void CurveEval::mark_cache_invalid()
}
}
static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
static HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
{
switch (dna_handle_type) {
case HD_FREE:
return BezierSpline::HandleType::Free;
return BEZIER_HANDLE_FREE;
case HD_AUTO:
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
case HD_VECT:
return BezierSpline::HandleType::Vector;
return BEZIER_HANDLE_VECTOR;
case HD_ALIGN:
return BezierSpline::HandleType::Align;
return BEZIER_HANDLE_ALIGN;
case HD_AUTO_ANIM:
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
case HD_ALIGN_DOUBLESIDE:
return BezierSpline::HandleType::Align;
return BEZIER_HANDLE_ALIGN;
}
BLI_assert_unreachable();
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
}
static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
@ -220,8 +220,8 @@ static SplinePtr spline_from_dna_bezier(const Nurb &nurb)
MutableSpan<float3> positions = spline->positions();
MutableSpan<float3> handle_positions_left = spline->handle_positions_left(true);
MutableSpan<float3> handle_positions_right = spline->handle_positions_right(true);
MutableSpan<BezierSpline::HandleType> handle_types_left = spline->handle_types_left();
MutableSpan<BezierSpline::HandleType> handle_types_right = spline->handle_types_right();
MutableSpan<int8_t> handle_types_left = spline->handle_types_left();
MutableSpan<int8_t> handle_types_right = spline->handle_types_right();
MutableSpan<float> radii = spline->radii();
MutableSpan<float> tilts = spline->tilts();

View File

@ -226,7 +226,7 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
}
/* Mark edge loops from sharp vector control points sharp. */
if (profile.type() == Spline::Type::Bezier) {
if (profile.type() == CURVE_TYPE_BEZIER) {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile);
Span<int> control_point_offsets = bezier_spline.control_point_offsets();
for (const int i : IndexRange(bezier_spline.size())) {

View File

@ -1959,10 +1959,10 @@ const CustomData_MeshMasks CD_MASK_BMESH = {
CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
};
const CustomData_MeshMasks CD_MASK_EVERYTHING = {
/* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL |
CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO |
CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX |
CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE),
/* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT |
CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO |
CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK |
CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE),
/* emask */
(CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT | CD_MASK_CREASE |
CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
@ -1971,7 +1971,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = {
CD_MASK_ORIGSPACE | CD_MASK_TANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_PREVIEW_MCOL |
CD_MASK_PROP_ALL),
/* pmask */
(CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_FACEMAP |
(CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_FACEMAP |
CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS),
/* lmask */
(CD_MASK_MLOOP | CD_MASK_BM_ELEM_PYPTR | CD_MASK_MDISPS | CD_MASK_NORMAL | CD_MASK_MLOOPUV |

View File

@ -3292,7 +3292,7 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds,
mpolys = me->mpoly;
mloops = me->mloop;
/* Get size (dimension) but considering scaling scaling. */
/* Get size (dimension) but considering scaling. */
copy_v3_v3(cell_size_scaled, fds->cell_size);
mul_v3_v3(cell_size_scaled, ob->scale);
madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, cell_size_scaled, fds->res_min);

View File

@ -420,15 +420,18 @@ static Array<float3> curve_normal_point_domain(const CurveEval &curve)
const Spline &spline = *splines[i];
MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
switch (splines[i]->type()) {
case Spline::Type::Bezier:
case CURVE_TYPE_BEZIER:
calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
break;
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
break;
case Spline::Type::NURBS:
case CURVE_TYPE_NURBS:
calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
break;
case CURVE_TYPE_CATMULL_ROM:
BLI_assert_unreachable();
break;
}
}
});
@ -447,7 +450,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const Attri
/* Use a reference to evaluated normals if possible to avoid an allocation and a copy.
* This is only possible when there is only one poly spline. */
if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
if (splines.size() == 1 && splines.first()->type() == CURVE_TYPE_POLY) {
const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
return VArray<float3>::ForSpan(spline.evaluated_normals());
}
@ -955,7 +958,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
{
const PointIndices indices = lookup_point_indices(offsets_, index);
const Spline &spline = *splines_[indices.spline_index];
if (spline.type() == Spline::Type::Bezier) {
if (spline.type() == CURVE_TYPE_BEZIER) {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
return is_right_ ? bezier_spline.handle_positions_right()[indices.point_index] :
bezier_spline.handle_positions_left()[indices.point_index];
@ -967,7 +970,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
{
const PointIndices indices = lookup_point_indices(offsets_, index);
Spline &spline = *splines_[indices.spline_index];
if (spline.type() == Spline::Type::Bezier) {
if (spline.type() == CURVE_TYPE_BEZIER) {
BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
if (is_right_) {
bezier_spline.set_handle_position_right(indices.point_index, value);
@ -983,7 +986,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
{
for (const int spline_index : splines_.index_range()) {
Spline &spline = *splines_[spline_index];
if (spline.type() == Spline::Type::Bezier) {
if (spline.type() == CURVE_TYPE_BEZIER) {
const int offset = offsets_[spline_index];
BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
@ -1024,7 +1027,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
{
Array<Span<float3>> spans(splines.size());
for (const int i : spans.index_range()) {
if (splines[i]->type() == Spline::Type::Bezier) {
if (splines[i]->type() == CURVE_TYPE_BEZIER) {
BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]);
spans[i] = is_right ? bezier_spline.handle_positions_right() :
bezier_spline.handle_positions_left();
@ -1214,7 +1217,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
/* Use the regular position virtual array when there aren't any Bezier splines
* to avoid the overhead of checking the spline type for every point. */
if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
}
@ -1255,7 +1258,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
return {};
}
if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
return {};
}
@ -1273,7 +1276,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
return {};
}
if (!curve->has_spline_with_type(Spline::Type::Bezier)) {
if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
return {};
}
@ -1304,7 +1307,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
return false;
}
return curve->has_spline_with_type(Spline::Type::Bezier) &&
return curve->has_spline_with_type(CURVE_TYPE_BEZIER) &&
component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
}
};

View File

@ -153,6 +153,7 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f
id_us_plus(dst_id->override_library->reference);
dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root;
dst_id->override_library->flag = src_id->override_library->flag;
if (do_full_copy) {
BLI_duplicatelist(&dst_id->override_library->properties,

View File

@ -59,6 +59,8 @@
#include "BLO_read_write.h"
using blender::float3;
static void mesh_clear_geometry(Mesh *mesh);
static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata);
@ -1106,16 +1108,6 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src,
mesh_tessface_clear_intern(me_dst, false);
}
me_dst->runtime.cd_dirty_poly = me_src->runtime.cd_dirty_poly;
me_dst->runtime.cd_dirty_vert = me_src->runtime.cd_dirty_vert;
/* Ensure that when no normal layers exist, they are marked dirty, because
* normals might not have been included in the mask of copied layers. */
if (!CustomData_has_layer(&me_dst->vdata, CD_NORMAL) ||
!CustomData_has_layer(&me_dst->pdata, CD_NORMAL)) {
BKE_mesh_normals_tag_dirty(me_dst);
}
/* The destination mesh should at least have valid primary CD layers,
* even in cases where the source mesh does not. */
mesh_ensure_cdlayers_primary(me_dst, do_tessface);
@ -1983,8 +1975,6 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac
nullptr);
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
mesh->runtime.cd_dirty_loop &= ~CD_MASK_NORMAL;
}
void BKE_mesh_calc_normals_split(Mesh *mesh)
@ -2162,6 +2152,10 @@ static void split_faces_split_new_verts(Mesh *mesh,
MVert *mvert = mesh->mvert;
float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(mesh);
/* Normals were already calculated at the beginning of this operation, we rely on that to update
* them partially here. */
BLI_assert(!BKE_mesh_vertex_normals_are_dirty(mesh));
/* Remember new_verts is a single linklist, so its items are in reversed order... */
MVert *new_mv = &mvert[mesh->totvert - 1];
for (int i = mesh->totvert - 1; i >= verts_len; i--, new_mv--, new_verts = new_verts->next) {
@ -2172,7 +2166,6 @@ static void split_faces_split_new_verts(Mesh *mesh,
copy_v3_v3(vert_normals[i], new_verts->vnor);
}
}
BKE_mesh_vertex_normals_clear_dirty(mesh);
}
/* Perform actual split of edges. */
@ -2243,6 +2236,10 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
/* Update pointers to a newly allocated memory. */
BKE_mesh_update_customdata_pointers(mesh, false);
/* Update normals manually to avoid recalculation after this operation. */
mesh->runtime.vert_normals = (float(*)[3])MEM_reallocN(mesh->runtime.vert_normals,
sizeof(float[3]) * mesh->totvert);
/* Perform actual split of vertices and edges. */
split_faces_split_new_verts(mesh, new_verts, num_new_verts);
if (do_edges) {

View File

@ -292,7 +292,7 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
r_info->mesh_edge_offset[mi] = e;
r_info->mesh_poly_offset[mi] = f;
/* Get matrix that transforms a coordinate in objects[mi]'s local space
* to the target space space. */
* to the target space. */
const float4x4 objn_mat = (obmats[mi] == nullptr) ? float4x4::identity() :
clean_obmat(*obmats[mi]);
r_info->to_target_transform[mi] = inv_target_mat * objn_mat;

View File

@ -1481,15 +1481,8 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
tmp.cd_flag = mesh_src->cd_flag;
tmp.runtime.deformed_only = mesh_src->runtime.deformed_only;
tmp.runtime.cd_dirty_poly = mesh_src->runtime.cd_dirty_poly;
tmp.runtime.cd_dirty_vert = mesh_src->runtime.cd_dirty_vert;
/* Ensure that when no normal layers exist, they are marked dirty, because
* normals might not have been included in the mask of copied layers. */
if (!CustomData_has_layer(&tmp.vdata, CD_NORMAL) ||
!CustomData_has_layer(&tmp.pdata, CD_NORMAL)) {
BKE_mesh_normals_tag_dirty(&tmp);
}
/* Clear the normals completely, since the new vertex / polygon count might be different. */
BKE_mesh_clear_derived_normals(&tmp);
if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) {
KeyBlock *kb;

View File

@ -94,53 +94,72 @@ static void add_v3_v3_atomic(float r[3], const float a[3])
void BKE_mesh_normals_tag_dirty(Mesh *mesh)
{
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
mesh->runtime.vert_normals_dirty = true;
mesh->runtime.poly_normals_dirty = true;
}
float (*BKE_mesh_vertex_normals_for_write(Mesh *mesh))[3]
{
CustomData_duplicate_referenced_layer(&mesh->vdata, CD_NORMAL, mesh->totvert);
return (float(*)[3])CustomData_add_layer(
&mesh->vdata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totvert);
if (mesh->runtime.vert_normals == nullptr) {
mesh->runtime.vert_normals = (float(*)[3])MEM_malloc_arrayN(
mesh->totvert, sizeof(float[3]), __func__);
}
BLI_assert(MEM_allocN_len(mesh->runtime.vert_normals) >= sizeof(float[3]) * mesh->totvert);
return mesh->runtime.vert_normals;
}
float (*BKE_mesh_poly_normals_for_write(Mesh *mesh))[3]
{
CustomData_duplicate_referenced_layer(&mesh->pdata, CD_NORMAL, mesh->totpoly);
return (float(*)[3])CustomData_add_layer(
&mesh->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totpoly);
if (mesh->runtime.poly_normals == nullptr) {
mesh->runtime.poly_normals = (float(*)[3])MEM_malloc_arrayN(
mesh->totpoly, sizeof(float[3]), __func__);
}
BLI_assert(MEM_allocN_len(mesh->runtime.poly_normals) >= sizeof(float[3]) * mesh->totpoly);
return mesh->runtime.poly_normals;
}
void BKE_mesh_vertex_normals_clear_dirty(Mesh *mesh)
{
mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL;
mesh->runtime.vert_normals_dirty = false;
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
}
void BKE_mesh_poly_normals_clear_dirty(Mesh *mesh)
{
mesh->runtime.cd_dirty_poly &= ~CD_MASK_NORMAL;
mesh->runtime.poly_normals_dirty = false;
BKE_mesh_assert_normals_dirty_or_calculated(mesh);
}
bool BKE_mesh_vertex_normals_are_dirty(const Mesh *mesh)
{
return mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL;
return mesh->runtime.vert_normals_dirty;
}
bool BKE_mesh_poly_normals_are_dirty(const Mesh *mesh)
{
return mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL;
return mesh->runtime.poly_normals_dirty;
}
void BKE_mesh_clear_derived_normals(Mesh *mesh)
{
MEM_SAFE_FREE(mesh->runtime.vert_normals);
MEM_SAFE_FREE(mesh->runtime.poly_normals);
mesh->runtime.vert_normals_dirty = true;
mesh->runtime.poly_normals_dirty = true;
}
void BKE_mesh_assert_normals_dirty_or_calculated(const Mesh *mesh)
{
if (!(mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL)) {
BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL) || mesh->totvert == 0);
if (!mesh->runtime.vert_normals_dirty) {
BLI_assert(mesh->runtime.vert_normals || mesh->totvert == 0);
}
if (!(mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL)) {
BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL) || mesh->totpoly == 0);
if (!mesh->runtime.poly_normals_dirty) {
BLI_assert(mesh->runtime.poly_normals || mesh->totpoly == 0);
}
}
@ -342,8 +361,8 @@ static void mesh_calc_normals_poly_and_vertex(MVert *mvert,
const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3]
{
if (!(BKE_mesh_vertex_normals_are_dirty(mesh) || BKE_mesh_poly_normals_are_dirty(mesh))) {
BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL) || mesh->totvert == 0);
return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL);
BLI_assert(mesh->runtime.vert_normals != nullptr || mesh->totvert == 0);
return mesh->runtime.vert_normals;
}
if (mesh->totvert == 0) {
@ -353,9 +372,9 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3]
ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex;
BLI_mutex_lock(normals_mutex);
if (!(BKE_mesh_vertex_normals_are_dirty(mesh) || BKE_mesh_poly_normals_are_dirty(mesh))) {
BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL));
BLI_assert(mesh->runtime.vert_normals != nullptr);
BLI_mutex_unlock(normals_mutex);
return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL);
return mesh->runtime.vert_normals;
}
float(*vert_normals)[3];
@ -388,8 +407,8 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3]
const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3]
{
if (!BKE_mesh_poly_normals_are_dirty(mesh)) {
BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL) || mesh->totpoly == 0);
return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL);
BLI_assert(mesh->runtime.poly_normals != nullptr || mesh->totpoly == 0);
return mesh->runtime.poly_normals;
}
if (mesh->totpoly == 0) {
@ -399,9 +418,9 @@ const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3]
ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex;
BLI_mutex_lock(normals_mutex);
if (!BKE_mesh_poly_normals_are_dirty(mesh)) {
BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL));
BLI_assert(mesh->runtime.poly_normals != nullptr);
BLI_mutex_unlock(normals_mutex);
return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL);
return mesh->runtime.poly_normals;
}
float(*poly_normals)[3];

View File

@ -88,6 +88,11 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag))
runtime->bvh_cache = NULL;
runtime->shrinkwrap_data = NULL;
runtime->vert_normals_dirty = true;
runtime->poly_normals_dirty = true;
runtime->vert_normals = NULL;
runtime->poly_normals = NULL;
mesh_runtime_init_mutexes(mesh);
}
@ -101,6 +106,7 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh)
BKE_mesh_runtime_clear_geometry(mesh);
BKE_mesh_batch_cache_free(mesh);
BKE_mesh_runtime_clear_edit_data(mesh);
BKE_mesh_clear_derived_normals(mesh);
}
/**

View File

@ -23,7 +23,7 @@ using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray;
Spline::Type Spline::type() const
CurveType Spline::type() const
{
return type_;
}
@ -34,15 +34,18 @@ void Spline::copy_base_settings(const Spline &src, Spline &dst)
dst.is_cyclic_ = src.is_cyclic_;
}
static SplinePtr create_spline(const Spline::Type type)
static SplinePtr create_spline(const CurveType type)
{
switch (type) {
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
return std::make_unique<PolySpline>();
case Spline::Type::Bezier:
case CURVE_TYPE_BEZIER:
return std::make_unique<BezierSpline>();
case Spline::Type::NURBS:
case CURVE_TYPE_NURBS:
return std::make_unique<NURBSpline>();
case CURVE_TYPE_CATMULL_ROM:
BLI_assert_unreachable();
return {};
}
BLI_assert_unreachable();
return {};

View File

@ -93,11 +93,11 @@ Span<float> BezierSpline::tilts() const
{
return tilts_;
}
Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const
Span<int8_t> BezierSpline::handle_types_left() const
{
return handle_types_left_;
}
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left()
MutableSpan<int8_t> BezierSpline::handle_types_left()
{
return handle_types_left_;
}
@ -114,11 +114,11 @@ MutableSpan<float3> BezierSpline::handle_positions_left(const bool write_only)
return handle_positions_left_;
}
Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const
Span<int8_t> BezierSpline::handle_types_right() const
{
return handle_types_right_;
}
MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right()
MutableSpan<int8_t> BezierSpline::handle_types_right()
{
return handle_types_right_;
}
@ -187,7 +187,7 @@ void BezierSpline::ensure_auto_handles() const
for (const int i : IndexRange(this->size())) {
using namespace blender;
if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) {
if (ELEM(BEZIER_HANDLE_AUTO, handle_types_left_[i], handle_types_right_[i])) {
const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i);
const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i];
float prev_len = math::length(prev_diff);
@ -203,23 +203,23 @@ void BezierSpline::ensure_auto_handles() const
/* This magic number is unfortunate, but comes from elsewhere in Blender. */
const float len = math::length(dir) * 2.5614f;
if (len != 0.0f) {
if (handle_types_left_[i] == HandleType::Auto) {
if (handle_types_left_[i] == BEZIER_HANDLE_AUTO) {
const float prev_len_clamped = std::min(prev_len, next_len * 5.0f);
handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len);
}
if (handle_types_right_[i] == HandleType::Auto) {
if (handle_types_right_[i] == BEZIER_HANDLE_AUTO) {
const float next_len_clamped = std::min(next_len, prev_len * 5.0f);
handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len);
}
}
}
if (handle_types_left_[i] == HandleType::Vector) {
if (handle_types_left_[i] == BEZIER_HANDLE_VECTOR) {
const float3 prev = previous_position(positions_, is_cyclic_, i);
handle_positions_left_[i] = math::interpolate(positions_[i], prev, 1.0f / 3.0f);
}
if (handle_types_right_[i] == HandleType::Vector) {
if (handle_types_right_[i] == BEZIER_HANDLE_VECTOR) {
const float3 next = next_position(positions_, is_cyclic_, i);
handle_positions_right_[i] = math::interpolate(positions_[i], next, 1.0f / 3.0f);
}
@ -257,8 +257,8 @@ void BezierSpline::transform(const blender::float4x4 &matrix)
}
static void set_handle_position(const float3 &position,
const BezierSpline::HandleType type,
const BezierSpline::HandleType type_other,
const HandleType type,
const HandleType type_other,
const float3 &new_value,
float3 &handle,
float3 &handle_other)
@ -266,12 +266,12 @@ static void set_handle_position(const float3 &position,
using namespace blender::math;
/* Don't bother when the handle positions are calculated automatically anyway. */
if (ELEM(type, BezierSpline::HandleType::Auto, BezierSpline::HandleType::Vector)) {
if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) {
return;
}
handle = new_value;
if (type_other == BezierSpline::HandleType::Align) {
if (type_other == BEZIER_HANDLE_ALIGN) {
/* Keep track of the old length of the opposite handle. */
const float length = distance(handle_other, position);
/* Set the other handle to directly opposite from the current handle. */
@ -283,8 +283,8 @@ static void set_handle_position(const float3 &position,
void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value)
{
set_handle_position(positions_[index],
handle_types_right_[index],
handle_types_left_[index],
static_cast<HandleType>(handle_types_right_[index]),
static_cast<HandleType>(handle_types_left_[index]),
value,
handle_positions_right_[index],
handle_positions_left_[index]);
@ -293,8 +293,8 @@ void BezierSpline::set_handle_position_right(const int index, const blender::flo
void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value)
{
set_handle_position(positions_[index],
handle_types_left_[index],
handle_types_right_[index],
static_cast<HandleType>(handle_types_right_[index]),
static_cast<HandleType>(handle_types_left_[index]),
value,
handle_positions_left_[index],
handle_positions_right_[index]);
@ -302,8 +302,8 @@ void BezierSpline::set_handle_position_left(const int index, const blender::floa
bool BezierSpline::point_is_sharp(const int index) const
{
return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) ||
ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free);
return ELEM(handle_types_left_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) ||
ELEM(handle_types_right_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE);
}
bool BezierSpline::segment_is_vector(const int index) const
@ -313,15 +313,15 @@ bool BezierSpline::segment_is_vector(const int index) const
if (index == this->size() - 1) {
if (is_cyclic_) {
return handle_types_right_.last() == HandleType::Vector &&
handle_types_left_.first() == HandleType::Vector;
return handle_types_right_.last() == BEZIER_HANDLE_VECTOR &&
handle_types_left_.first() == BEZIER_HANDLE_VECTOR;
}
/* There is actually no segment in this case, but it's nice to avoid
* having a special case for the last segment in calling code. */
return true;
}
return handle_types_right_[index] == HandleType::Vector &&
handle_types_left_[index + 1] == HandleType::Vector;
return handle_types_right_[index] == BEZIER_HANDLE_VECTOR &&
handle_types_left_[index + 1] == BEZIER_HANDLE_VECTOR;
}
void BezierSpline::mark_cache_invalid()

View File

@ -72,6 +72,9 @@ static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx)
static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vertices)
{
if (!ctx->have_displacement) {
return;
}
ctx->accumulated_counters = MEM_calloc_arrayN(
num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters");
}
@ -424,18 +427,17 @@ static void subdiv_accumulate_vertex_displacement(SubdivMeshContext *ctx,
const float v,
MVert *subdiv_vert)
{
/* Accumulate displacement. */
Subdiv *subdiv = ctx->subdiv;
const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert;
float dummy_P[3], dPdu[3], dPdv[3], D[3];
BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
/* Accumulate displacement if needed. */
if (ctx->have_displacement) {
/* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero
* locations as a default calloc(). */
BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
add_v3_v3(subdiv_vert->co, D);
}
/* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero
* locations as a default calloc(). */
BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
add_v3_v3(subdiv_vert->co, D);
if (ctx->accumulated_counters) {
++ctx->accumulated_counters[subdiv_vertex_index];
}
@ -554,12 +556,13 @@ static void evaluate_vertex_and_apply_displacement_interpolate(
add_v3_v3(subdiv_vert->co, D);
}
static void subdiv_mesh_vertex_every_corner_or_edge(const SubdivForeachContext *foreach_context,
void *UNUSED(tls),
const int ptex_face_index,
const float u,
const float v,
const int subdiv_vertex_index)
static void subdiv_mesh_vertex_displacement_every_corner_or_edge(
const SubdivForeachContext *foreach_context,
void *UNUSED(tls),
const int ptex_face_index,
const float u,
const float v,
const int subdiv_vertex_index)
{
SubdivMeshContext *ctx = foreach_context->user_data;
Mesh *subdiv_mesh = ctx->subdiv_mesh;
@ -568,31 +571,32 @@ static void subdiv_mesh_vertex_every_corner_or_edge(const SubdivForeachContext *
subdiv_accumulate_vertex_displacement(ctx, ptex_face_index, u, v, subdiv_vert);
}
static void subdiv_mesh_vertex_every_corner(const SubdivForeachContext *foreach_context,
void *tls,
const int ptex_face_index,
const float u,
const float v,
const int UNUSED(coarse_vertex_index),
const int UNUSED(coarse_poly_index),
const int UNUSED(coarse_corner),
const int subdiv_vertex_index)
static void subdiv_mesh_vertex_displacement_every_corner(
const SubdivForeachContext *foreach_context,
void *tls,
const int ptex_face_index,
const float u,
const float v,
const int UNUSED(coarse_vertex_index),
const int UNUSED(coarse_poly_index),
const int UNUSED(coarse_corner),
const int subdiv_vertex_index)
{
subdiv_mesh_vertex_every_corner_or_edge(
subdiv_mesh_vertex_displacement_every_corner_or_edge(
foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index);
}
static void subdiv_mesh_vertex_every_edge(const SubdivForeachContext *foreach_context,
void *tls,
const int ptex_face_index,
const float u,
const float v,
const int UNUSED(coarse_edge_index),
const int UNUSED(coarse_poly_index),
const int UNUSED(coarse_corner),
const int subdiv_vertex_index)
static void subdiv_mesh_vertex_displacement_every_edge(const SubdivForeachContext *foreach_context,
void *tls,
const int ptex_face_index,
const float u,
const float v,
const int UNUSED(coarse_edge_index),
const int UNUSED(coarse_poly_index),
const int UNUSED(coarse_corner),
const int subdiv_vertex_index)
{
subdiv_mesh_vertex_every_corner_or_edge(
subdiv_mesh_vertex_displacement_every_corner_or_edge(
foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index);
}
@ -1078,12 +1082,8 @@ static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context,
foreach_context->topology_info = subdiv_mesh_topology_info;
/* Every boundary geometry. Used for displacement averaging. */
if (subdiv_context->have_displacement) {
foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner;
foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge;
}
else {
foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner;
foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge;
foreach_context->vertex_every_corner = subdiv_mesh_vertex_displacement_every_corner;
foreach_context->vertex_every_edge = subdiv_mesh_vertex_displacement_every_edge;
}
foreach_context->vertex_corner = subdiv_mesh_vertex_corner;
foreach_context->vertex_edge = subdiv_mesh_vertex_edge;

View File

@ -960,9 +960,14 @@ static int start_ffmpeg_impl(FFMpegContext *context,
break;
}
/* Returns after this must 'goto fail;' */
/* Returns after this must 'goto fail;' */
# if LIBAVFORMAT_VERSION_MAJOR >= 59
of->oformat = fmt;
# else
/* *DEPRECATED* 2022/08/01 For FFMPEG (<5.0) remove this else branch and the `ifdef` above. */
of->oformat = (AVOutputFormat *)fmt;
# endif
if (video_codec == AV_CODEC_ID_DVVIDEO) {
if (rectx != 720) {

View File

@ -298,7 +298,7 @@ bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL();
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL();
/**
* Replaces "#" character sequence in last slash-separated component of `path`
* with frame as decimal integer, with leading zeroes as necessary, to make digits digits.
* with frame as decimal integer, with leading zeroes as necessary, to make digits.
*/
bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL();
/**

View File

@ -240,6 +240,7 @@ set(SRC
BLI_math_vec_mpq_types.hh
BLI_math_vec_types.hh
BLI_math_vector.h
BLI_math_vector.hh
BLI_memarena.h
BLI_memblock.h
BLI_memiter.h
@ -308,6 +309,9 @@ set(SRC
BLI_winstuff.h
PIL_time.h
PIL_time_utildefines.h
# Without these files listed, they aren't known to CMake.
../../../extern/json/include/json.hpp
)
set(LIB

View File

@ -2185,7 +2185,7 @@ static void finish_patch_cell_graph(const IMesh &tm,
* There will be a vector of \a nshapes winding numbers in each cell, one per
* input shape.
* As one crosses a patch into a new cell, the original shape (mesh part)
* that that patch was part of dictates which winding number changes.
* that patch was part of dictates which winding number changes.
* The shape_fn(triangle_number) function should return the shape that the
* triangle is part of.
* Also, as soon as the winding numbers for a cell are set, use bool_optype

View File

@ -1186,7 +1186,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
BKE_mesh_normals_tag_dirty(me);
/* Clear normals on the mesh completely, since the original vertex and polygon count might be
* different than the BMesh's. */
BKE_mesh_clear_derived_normals(me);
me->runtime.deformed_only = true;

View File

@ -532,7 +532,7 @@ static BevVert *find_bevvert(BevelParams *bp, BMVert *bmv)
/**
* Find the EdgeHalf representing the other end of e->e.
* \return Return other end's BevVert in *r_bvother, if r_bvother is provided. That may not have
* \return other end's BevVert in *r_bvother, if r_bvother is provided. That may not have
* been constructed yet, in which case return NULL.
*/
static EdgeHalf *find_other_end_edge_half(BevelParams *bp, EdgeHalf *e, BevVert **r_bvother)

View File

@ -221,6 +221,8 @@ set(SRC
engines/image/image_space_image.hh
engines/image/image_space_node.hh
engines/image/image_space.hh
engines/image/image_texture_info.hh
engines/image/image_usage.hh
engines/image/image_wrappers.hh
engines/workbench/workbench_engine.h
engines/workbench/workbench_private.h

View File

@ -27,25 +27,12 @@
static void eevee_motion_blur_mesh_data_free(void *val)
{
EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val;
EEVEE_HairMotionData *hair_mb = (EEVEE_HairMotionData *)val;
switch (geom_mb->type) {
case EEVEE_MOTION_DATA_HAIR:
for (int j = 0; j < hair_mb->psys_len; j++) {
for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
GPU_VERTBUF_DISCARD_SAFE(hair_mb->psys[j].hair_pos[i]);
}
for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) {
DRW_TEXTURE_FREE_SAFE(hair_mb->psys[j].hair_pos_tx[i]);
}
}
break;
case EEVEE_MOTION_DATA_MESH:
for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) {
GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]);
}
break;
EEVEE_ObjectMotionData *mb_data = (EEVEE_ObjectMotionData *)val;
if (mb_data->hair_data != NULL) {
MEM_freeN(mb_data->hair_data);
}
if (mb_data->geometry_data != NULL) {
MEM_freeN(mb_data->geometry_data);
}
MEM_freeN(val);
}
@ -84,39 +71,57 @@ static bool eevee_object_key_cmp(const void *a, const void *b)
return false;
}
void EEVEE_motion_hair_step_free(EEVEE_HairMotionStepData *step_data)
{
GPU_vertbuf_discard(step_data->hair_pos);
DRW_texture_free(step_data->hair_pos_tx);
MEM_freeN(step_data);
}
void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb)
{
if (mb->object == NULL) {
mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion");
}
if (mb->geom == NULL) {
mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion");
for (int i = 0; i < 2; i++) {
if (mb->position_vbo_cache[i] == NULL) {
mb->position_vbo_cache[i] = BLI_ghash_new(
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE duplicate vbo cache");
}
if (mb->hair_motion_step_cache[i] == NULL) {
mb->hair_motion_step_cache[i] = BLI_ghash_new(
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE hair motion step cache");
}
}
}
void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb)
{
if (mb->object) {
BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN);
BLI_ghash_free(mb->object, MEM_freeN, eevee_motion_blur_mesh_data_free);
mb->object = NULL;
}
if (mb->geom) {
BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free);
mb->geom = NULL;
for (int i = 0; i < 2; i++) {
if (mb->position_vbo_cache[i]) {
BLI_ghash_free(mb->position_vbo_cache[i], NULL, (GHashValFreeFP)GPU_vertbuf_discard);
}
if (mb->hair_motion_step_cache[i]) {
BLI_ghash_free(
mb->hair_motion_step_cache[i], NULL, (GHashValFreeFP)EEVEE_motion_hair_step_free);
}
}
}
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
Object *ob,
bool hair)
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob)
{
if (mb->object == NULL) {
return NULL;
}
EEVEE_ObjectKey key, *key_p;
/* Small hack to avoid another comparison. */
key.ob = (Object *)((char *)ob + hair);
/* Assumes that all instances have the same object pointer. This is currently the case because
* instance objects are temporary objects on the stack. */
key.ob = ob;
DupliObject *dup = DRW_object_get_dupli(ob);
if (dup) {
key.parent = DRW_object_get_dupli_parent(ob);
@ -139,53 +144,28 @@ EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *
return ob_step;
}
static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, bool hair)
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data)
{
if (mb->geom == NULL) {
return NULL;
if (mb_data->geometry_data == NULL) {
EEVEE_GeometryMotionData *geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
geom_step->type = EEVEE_MOTION_DATA_MESH;
mb_data->geometry_data = geom_step;
}
DupliObject *dup = DRW_object_get_dupli(ob);
void *key;
if (dup) {
key = dup->ob;
}
else {
key = ob;
}
/* Only use data for object that have no modifiers. */
if (!BKE_object_is_modified(DRW_context_state_get()->scene, ob)) {
key = ob->data;
}
key = (char *)key + (int)hair;
EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key);
if (geom_step == NULL) {
if (hair) {
EEVEE_HairMotionData *hair_step;
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1;
hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len,
__func__);
hair_step->psys_len = psys_len;
geom_step = (EEVEE_GeometryMotionData *)hair_step;
geom_step->type = EEVEE_MOTION_DATA_HAIR;
}
else {
geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__);
geom_step->type = EEVEE_MOTION_DATA_MESH;
}
BLI_ghash_insert(mb->geom, key, geom_step);
}
return geom_step;
return mb_data->geometry_data;
}
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob)
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob)
{
return motion_blur_deform_data_get(mb, ob, false);
}
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob)
{
return motion_blur_deform_data_get(mb, ob, true);
if (mb_data->hair_data == NULL) {
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1;
EEVEE_HairMotionData *hair_step = MEM_callocN(
sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__);
hair_step->psys_len = psys_len;
hair_step->type = EEVEE_MOTION_DATA_HAIR;
mb_data->hair_data = hair_step;
}
return mb_data->hair_data;
}
/* View Layer data. */

View File

@ -226,15 +226,14 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
}
/* For now we assume hair objects are always moving. */
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
&effects->motion_blur, ob, true);
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob);
if (mb_data) {
int mb_step = effects->motion_blur_step;
/* Store transform. */
DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]);
EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(&effects->motion_blur, ob);
EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(mb_data, ob);
int psys_id = (md != NULL) ? BLI_findindex(&ob->modifiers, md) : 0;
if (psys_id >= mb_hair->psys_len) {
@ -252,8 +251,8 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
}
GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV];
GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT];
GPUTexture *tex_prev = mb_hair->psys[psys_id].step_data[MB_PREV].hair_pos_tx;
GPUTexture *tex_next = mb_hair->psys[psys_id].step_data[MB_NEXT].hair_pos_tx;
grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL);
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
@ -265,7 +264,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
}
else {
/* Store vertex position buffer. */
mb_hair->psys[psys_id].hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md);
mb_hair->psys[psys_id].step_data[mb_step].hair_pos = DRW_hair_pos_buffer_get(ob, psys, md);
mb_hair->use_deform = true;
}
}
@ -304,24 +303,14 @@ void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
return;
}
const DupliObject *dup = DRW_object_get_dupli(ob);
if (dup != NULL && dup->ob->data != dup->ob_data) {
/* Geometry instances do not support motion blur correctly yet. The #key used in
* #motion_blur_deform_data_get has to take ids of instances (#DupliObject.persistent_id) into
* account. Otherwise it can't find matching geometry instances at different points in time. */
return;
}
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(
&effects->motion_blur, ob, false);
EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob);
if (mb_data) {
int mb_step = effects->motion_blur_step;
/* Store transform. */
copy_m4_m4(mb_data->obmat[mb_step], ob->obmat);
EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(&effects->motion_blur,
ob);
EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(mb_data);
if (mb_step == MB_CURR) {
GPUBatch *batch = DRW_cache_object_surface_get(ob);
@ -407,86 +396,93 @@ void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata)
DRW_cache_restart();
}
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object);
BLI_ghashIterator_done(&ghi) == false;
BLI_ghashIterator_step(&ghi)) {
EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom;
if (!mb_geom->use_deform) {
continue;
}
switch (mb_geom->type) {
case EEVEE_MOTION_DATA_HAIR:
if (mb_step == MB_CURR) {
/* TODO(fclem): Check if vertex count mismatch. */
mb_hair->use_deform = true;
}
else {
for (int i = 0; i < mb_hair->psys_len; i++) {
if (mb_hair->psys[i].hair_pos[mb_step] == NULL) {
continue;
}
mb_hair->psys[i].hair_pos[mb_step] = GPU_vertbuf_duplicate(
mb_hair->psys[i].hair_pos[mb_step]);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(mb_hair->psys[i].hair_pos[mb_step]);
mb_hair->psys[i].hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf(
"hair_pos_motion_blur", mb_hair->psys[i].hair_pos[mb_step]);
EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
EEVEE_HairMotionData *mb_hair = mb_data->hair_data;
EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data;
if (mb_hair != NULL && mb_hair->use_deform) {
if (mb_step == MB_CURR) {
/* TODO(fclem): Check if vertex count mismatch. */
mb_hair->use_deform = true;
}
else {
for (int i = 0; i < mb_hair->psys_len; i++) {
GPUVertBuf *vbo = mb_hair->psys[i].step_data[mb_step].hair_pos;
if (vbo == NULL) {
continue;
}
EEVEE_HairMotionStepData **step_data_cache_ptr;
if (!BLI_ghash_ensure_p(effects->motion_blur.hair_motion_step_cache[mb_step],
vbo,
(void ***)&step_data_cache_ptr)) {
EEVEE_HairMotionStepData *new_step_data = MEM_callocN(sizeof(EEVEE_HairMotionStepData),
__func__);
/* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */
new_step_data->hair_pos = GPU_vertbuf_duplicate(vbo);
/* Create vbo immediately to bind to texture buffer. */
GPU_vertbuf_use(new_step_data->hair_pos);
new_step_data->hair_pos_tx = GPU_texture_create_from_vertbuf("hair_pos_motion_blur",
new_step_data->hair_pos);
*step_data_cache_ptr = new_step_data;
}
mb_hair->psys[i].step_data[mb_step] = **step_data_cache_ptr;
}
break;
case EEVEE_MOTION_DATA_MESH:
if (mb_step == MB_CURR) {
/* Modify batch to have data from adjacent frames. */
GPUBatch *batch = mb_geom->batch;
for (int i = 0; i < MB_CURR; i++) {
GPUVertBuf *vbo = mb_geom->vbo[i];
if (vbo && batch) {
if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) {
/* Vertex count mismatch, disable deform motion blur. */
mb_geom->use_deform = false;
}
if (mb_geom->use_deform == false) {
motion_blur_remove_vbo_reference_from_batch(
batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]);
break;
}
}
}
if (mb_geom != NULL && mb_geom->use_deform) {
if (mb_step == MB_CURR) {
/* Modify batch to have data from adjacent frames. */
GPUBatch *batch = mb_geom->batch;
for (int i = 0; i < MB_CURR; i++) {
GPUVertBuf *vbo = mb_geom->vbo[i];
if (vbo && batch) {
if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) {
/* Vertex count mismatch, disable deform motion blur. */
mb_geom->use_deform = false;
}
if (mb_geom->use_deform == false) {
motion_blur_remove_vbo_reference_from_batch(
batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
break;
}
/* Avoid adding the same vbo more than once when the batch is used by multiple
* instances. */
if (!GPU_batch_vertbuf_has(batch, vbo)) {
/* Currently, the code assumes that all objects that share the same mesh in the
* current frame also share the same mesh on other frames. */
GPU_batch_vertbuf_add_ex(batch, vbo, false);
}
}
}
else {
GPUVertBuf *vbo = mb_geom->vbo[mb_step];
if (vbo) {
/* Use the vbo to perform the copy on the GPU. */
GPU_vertbuf_use(vbo);
/* Perform a copy to avoid losing it after RE_engine_frame_set(). */
mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo);
}
else {
GPUVertBuf *vbo = mb_geom->vbo[mb_step];
if (vbo) {
/* Use the vbo to perform the copy on the GPU. */
GPU_vertbuf_use(vbo);
/* Perform a copy to avoid losing it after RE_engine_frame_set(). */
GPUVertBuf **vbo_cache_ptr;
if (!BLI_ghash_ensure_p(effects->motion_blur.position_vbo_cache[mb_step],
vbo,
(void ***)&vbo_cache_ptr)) {
/* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */
GPUVertBuf *duplicated_vbo = GPU_vertbuf_duplicate(vbo);
*vbo_cache_ptr = duplicated_vbo;
/* Find and replace "pos" attrib name. */
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(duplicated_vbo);
int attrib_id = GPU_vertformat_attr_id_get(format, "pos");
GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt");
}
else {
/* This might happen if the object visibility has been animated. */
mb_geom->use_deform = false;
}
mb_geom->vbo[mb_step] = vbo = *vbo_cache_ptr;
}
break;
default:
BLI_assert(0);
break;
else {
/* This might happen if the object visibility has been animated. */
mb_geom->use_deform = false;
}
}
}
}
}
@ -503,54 +499,62 @@ void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata)
/* Camera Data. */
effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_NEXT];
/* Object Data. */
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object);
BLI_ghashIterator_done(&ghi) == false;
BLI_ghashIterator_step(&ghi)) {
EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
/* Swap #position_vbo_cache pointers. */
if (effects->motion_blur.position_vbo_cache[MB_PREV]) {
BLI_ghash_free(effects->motion_blur.position_vbo_cache[MB_PREV],
NULL,
(GHashValFreeFP)GPU_vertbuf_discard);
}
effects->motion_blur.position_vbo_cache[MB_PREV] =
effects->motion_blur.position_vbo_cache[MB_NEXT];
effects->motion_blur.position_vbo_cache[MB_NEXT] = NULL;
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
/* Swap #hair_motion_step_cache pointers. */
if (effects->motion_blur.hair_motion_step_cache[MB_PREV]) {
BLI_ghash_free(effects->motion_blur.hair_motion_step_cache[MB_PREV],
NULL,
(GHashValFreeFP)EEVEE_motion_hair_step_free);
}
effects->motion_blur.hair_motion_step_cache[MB_PREV] =
effects->motion_blur.hair_motion_step_cache[MB_NEXT];
effects->motion_blur.hair_motion_step_cache[MB_NEXT] = NULL;
/* Rename attributes in #position_vbo_cache. */
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.position_vbo_cache[MB_PREV]);
!BLI_ghashIterator_done(&ghi);
BLI_ghashIterator_step(&ghi)) {
GPUVertBuf *vbo = BLI_ghashIterator_getValue(&ghi);
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
int attrib_id = GPU_vertformat_attr_id_get(format, "nxt");
GPU_vertformat_attr_rename(format, attrib_id, "prv");
}
/* Deformation Data. */
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom);
BLI_ghashIterator_done(&ghi) == false;
/* Object Data. */
for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); !BLI_ghashIterator_done(&ghi);
BLI_ghashIterator_step(&ghi)) {
EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi);
EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom;
EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi);
EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data;
EEVEE_HairMotionData *mb_hair = mb_data->hair_data;
switch (mb_geom->type) {
case EEVEE_MOTION_DATA_HAIR:
for (int i = 0; i < mb_hair->psys_len; i++) {
GPU_VERTBUF_DISCARD_SAFE(mb_hair->psys[i].hair_pos[MB_PREV]);
DRW_TEXTURE_FREE_SAFE(mb_hair->psys[i].hair_pos_tx[MB_PREV]);
mb_hair->psys[i].hair_pos[MB_PREV] = mb_hair->psys[i].hair_pos[MB_NEXT];
mb_hair->psys[i].hair_pos_tx[MB_PREV] = mb_hair->psys[i].hair_pos_tx[MB_NEXT];
mb_hair->psys[i].hair_pos[MB_NEXT] = NULL;
mb_hair->psys[i].hair_pos_tx[MB_NEXT] = NULL;
}
break;
copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]);
case EEVEE_MOTION_DATA_MESH:
if (mb_geom->batch != NULL) {
motion_blur_remove_vbo_reference_from_batch(
mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
}
GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]);
mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
mb_geom->vbo[MB_NEXT] = NULL;
if (mb_geom->vbo[MB_PREV]) {
GPUVertBuf *vbo = mb_geom->vbo[MB_PREV];
GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo);
int attrib_id = GPU_vertformat_attr_id_get(format, "nxt");
GPU_vertformat_attr_rename(format, attrib_id, "prv");
}
break;
default:
BLI_assert(0);
break;
if (mb_hair != NULL) {
for (int i = 0; i < mb_hair->psys_len; i++) {
mb_hair->psys[i].step_data[MB_PREV].hair_pos =
mb_hair->psys[i].step_data[MB_NEXT].hair_pos;
mb_hair->psys[i].step_data[MB_PREV].hair_pos_tx =
mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx;
mb_hair->psys[i].step_data[MB_NEXT].hair_pos = NULL;
mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx = NULL;
}
}
if (mb_geom != NULL) {
if (mb_geom->batch != NULL) {
motion_blur_remove_vbo_reference_from_batch(
mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]);
}
mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT];
mb_geom->vbo[MB_NEXT] = NULL;
}
}
}

View File

@ -618,8 +618,23 @@ enum {
#define MB_CURR 2
typedef struct EEVEE_MotionBlurData {
/**
* Maps #EEVEE_ObjectKey to #EEVEE_ObjectMotionData.
*/
struct GHash *object;
struct GHash *geom;
/**
* Maps original #GPUVertBuf to duplicated #GPUVertBuf.
* There are two maps for #MB_PREV and #MB_NEXT.
* Only the values are owned.
*/
struct GHash *position_vbo_cache[2];
/**
* Maps original #GPUVertBuf to #EEVEE_HairMotionStepData.
* There are two maps for #MB_PREV and #MB_NEXT.
* Only the values are owned.
*/
struct GHash *hair_motion_step_cache[2];
struct {
float viewmat[4][4];
float persmat[4][4];
@ -637,15 +652,16 @@ typedef struct EEVEE_ObjectKey {
int id[8]; /* MAX_DUPLI_RECUR */
} EEVEE_ObjectKey;
typedef struct EEVEE_ObjectMotionData {
float obmat[3][4][4];
} EEVEE_ObjectMotionData;
typedef enum eEEVEEMotionData {
EEVEE_MOTION_DATA_MESH = 0,
EEVEE_MOTION_DATA_HAIR,
} eEEVEEMotionData;
typedef struct EEVEE_HairMotionStepData {
struct GPUVertBuf *hair_pos;
struct GPUTexture *hair_pos_tx;
} EEVEE_HairMotionStepData;
typedef struct EEVEE_HairMotionData {
/** Needs to be first to ensure casting. */
eEEVEEMotionData type;
@ -653,8 +669,8 @@ typedef struct EEVEE_HairMotionData {
/** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */
int psys_len;
struct {
struct GPUVertBuf *hair_pos[2]; /* Position buffer for time = t +/- step. */
struct GPUTexture *hair_pos_tx[2]; /* Buffer Texture of the corresponding VBO. */
/* The vbos and textures are not owned. */
EEVEE_HairMotionStepData step_data[2]; /* Data for time = t +/- step. */
} psys[0];
} EEVEE_HairMotionData;
@ -664,10 +680,18 @@ typedef struct EEVEE_GeometryMotionData {
/** To disable deform mb if vertcount mismatch. */
int use_deform;
/* The batch and vbos are not owned. */
struct GPUBatch *batch; /* Batch for time = t. */
struct GPUVertBuf *vbo[2]; /* VBO for time = t +/- step. */
} EEVEE_GeometryMotionData;
typedef struct EEVEE_ObjectMotionData {
float obmat[3][4][4];
EEVEE_GeometryMotionData *geometry_data;
EEVEE_HairMotionData *hair_data;
} EEVEE_ObjectMotionData;
/* ************ EFFECTS DATA ************* */
typedef enum EEVEE_EffectsFlag {
@ -1062,17 +1086,15 @@ typedef struct EEVEE_PrivateData {
void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb);
void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb);
void EEVEE_view_layer_data_free(void *storage);
void EEVEE_motion_hair_step_free(EEVEE_HairMotionStepData *step_data);
EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer);
EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void);
EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob);
EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob);
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb,
Object *ob,
bool hair);
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb,
Object *ob);
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob);
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob);
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data);
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob);
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob);

View File

@ -4601,7 +4601,7 @@ void ANIM_channel_draw(
/* Draw slider:
* - Even if we can draw sliders for this view,
* we must also check that the channel-type supports them
* (only only F-Curves really can support them for now).
* (only F-Curves really can support them for now).
* - Slider should start before the toggles (if they're visible)
* to keep a clean line down the side.
*/
@ -5336,7 +5336,7 @@ void ANIM_channel_draw_widgets(const bContext *C,
/* Draw slider:
* - Even if we can draw sliders for this view, we must also check that the channel-type
* supports them (only only F-Curves really can support them for now).
* supports them (only F-Curves really can support them for now).
* - To make things easier, we use RNA-autobuts for this so that changes are
* reflected immediately, wherever they occurred.
* BUT, we don't use the layout engine, otherwise we'd get wrong alignment,

View File

@ -1097,7 +1097,7 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf)
/**
* Naive dilate
*
* Expand green areas into enclosing red areas.
* Expand green areas into enclosing red or transparent areas.
* Using stack prevents creep when replacing colors directly.
* <pre>
* -----------
@ -1110,8 +1110,8 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf)
*/
static bool dilate_shape(ImBuf *ibuf)
{
#define IS_RED (color[0] == 1.0f)
#define IS_GREEN (color[1] == 1.0f)
#define IS_NOT_GREEN (color[1] != 1.0f)
bool done = false;
@ -1140,7 +1140,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (v - 1 >= 0) {
index = v - 1;
get_pixel(ibuf, index, color);
if (IS_RED) {
if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
lt = index;
}
@ -1149,7 +1149,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (v + 1 <= maxpixel) {
index = v + 1;
get_pixel(ibuf, index, color);
if (IS_RED) {
if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
rt = index;
}
@ -1158,7 +1158,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (v + ibuf->x <= max_size) {
index = v + ibuf->x;
get_pixel(ibuf, index, color);
if (IS_RED) {
if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
tp = index;
}
@ -1167,7 +1167,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (v - ibuf->x >= 0) {
index = v - ibuf->x;
get_pixel(ibuf, index, color);
if (IS_RED) {
if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
bm = index;
}
@ -1176,7 +1176,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (tp && lt) {
index = tp - 1;
get_pixel(ibuf, index, color);
if (IS_RED) {
if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
}
}
@ -1184,7 +1184,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (tp && rt) {
index = tp + 1;
get_pixel(ibuf, index, color);
if (IS_RED) {
if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
}
}
@ -1192,7 +1192,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (bm && lt) {
index = bm - 1;
get_pixel(ibuf, index, color);
if (IS_RED) {
if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
}
}
@ -1200,7 +1200,7 @@ static bool dilate_shape(ImBuf *ibuf)
if (bm && rt) {
index = bm + 1;
get_pixel(ibuf, index, color);
if (IS_RED) {
if (IS_NOT_GREEN) {
BLI_stack_push(stack, &index);
}
}
@ -1218,8 +1218,8 @@ static bool dilate_shape(ImBuf *ibuf)
return done;
#undef IS_RED
#undef IS_GREEN
#undef IS_NOT_GREEN
}
/**

View File

@ -250,6 +250,12 @@ void ED_area_offscreen_free(struct wmWindowManager *wm,
struct wmWindow *win,
struct ScrArea *area);
/**
* Search all screens, even non-active or overlapping (multiple windows), return the most-likely
* area of interest. xy is relative to active window, like all similar functions.
*/
ScrArea *ED_area_find_under_cursor(const struct bContext *C, int spacetype, const int xy[2]);
ScrArea *ED_screen_areas_iter_first(const struct wmWindow *win, const bScreen *screen);
ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area);
/**

View File

@ -1418,7 +1418,7 @@ static bool ui_but_event_property_operator_string(const bContext *C,
}
/* This version is only for finding hotkeys for properties.
* These are set set via a data-path which is appended to the context,
* These are set via a data-path which is appended to the context,
* manipulated using operators (see #ctx_toggle_opnames). */
if (ptr->owner_id) {

View File

@ -598,7 +598,7 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha
}
/**
* \note Note that \a layout_type may be null.
* \note that \a layout_type may be null.
*/
static uiList *ui_list_ensure(bContext *C,
uiListType *ui_list_type,

View File

@ -2579,7 +2579,7 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp
*
* A lot of places of the UI like the Node Editor or panels are zoomable. In most cases we can
* get the zoom factor from the aspect, but in some cases like popups we need to fall back to
* using the the size of the element. The latter method relies on the element always being the same
* using the size of the element. The latter method relies on the element always being the same
* size.
* \{ */

View File

@ -21,6 +21,7 @@
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_report.h"
#include "DEG_depsgraph.h"
@ -1110,6 +1111,8 @@ static void mesh_add_verts(Mesh *mesh, int len)
mesh->vdata = vdata;
BKE_mesh_update_customdata_pointers(mesh, false);
BKE_mesh_runtime_clear_cache(mesh);
/* scan the input list and insert the new vertices */
/* set default flags */
@ -1146,6 +1149,8 @@ static void mesh_add_edges(Mesh *mesh, int len)
mesh->edata = edata;
BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */
BKE_mesh_runtime_clear_cache(mesh);
/* set default flags */
medge = &mesh->medge[mesh->totedge];
for (i = 0; i < len; i++, medge++) {
@ -1174,6 +1179,8 @@ static void mesh_add_loops(Mesh *mesh, int len)
CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop);
}
BKE_mesh_runtime_clear_cache(mesh);
CustomData_free(&mesh->ldata, mesh->totloop);
mesh->ldata = ldata;
BKE_mesh_update_customdata_pointers(mesh, true);
@ -1205,6 +1212,8 @@ static void mesh_add_polys(Mesh *mesh, int len)
mesh->pdata = pdata;
BKE_mesh_update_customdata_pointers(mesh, true);
BKE_mesh_runtime_clear_cache(mesh);
/* set default flags */
mpoly = &mesh->mpoly[mesh->totpoly];
for (i = 0; i < len; i++, mpoly++) {

View File

@ -89,7 +89,7 @@
#include "CLG_log.h"
/* For menu/popup icons etc etc. */
/* For menu/popup icons etc. */
#include "UI_interface.h"
#include "UI_resources.h"

View File

@ -3438,6 +3438,38 @@ bool ED_area_is_global(const ScrArea *area)
return area->global != NULL;
}
ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int xy[2])
{
bScreen *screen = CTX_wm_screen(C);
wmWindow *win = CTX_wm_window(C);
wmWindowManager *wm = CTX_wm_manager(C);
ScrArea *area = NULL;
if (win->parent) {
/* If active window is a child, check itself first. */
area = BKE_screen_find_area_xy(screen, spacetype, xy);
}
if (!area) {
/* Check all windows except the active one. */
int scr_pos[2];
wmWindow *r_win = WM_window_find_under_cursor(wm, win, win, xy, scr_pos);
if (r_win) {
win = r_win;
screen = WM_window_get_active_screen(win);
area = BKE_screen_find_area_xy(screen, spacetype, scr_pos);
}
}
if (!area && !win->parent) {
/* If active window is a parent window, check itself last. */
area = BKE_screen_find_area_xy(screen, spacetype, xy);
}
return area;
}
ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen)
{
ScrArea *global_area = win->global_areas.areabase.first;

View File

@ -1008,9 +1008,6 @@ static void actionzone_exit(wmOperator *op)
static void actionzone_apply(bContext *C, wmOperator *op, int type)
{
wmWindow *win = CTX_wm_window(C);
sActionzoneData *sad = op->customdata;
sad->modifier = RNA_int_get(op->ptr, "modifier");
wmEvent event;
wm_event_init_from_window(win, &event);
@ -1051,6 +1048,7 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
sad->az = az;
sad->x = event->xy[0];
sad->y = event->xy[1];
sad->modifier = RNA_int_get(op->ptr, "modifier");
/* region azone directly reacts on mouse clicks */
if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) {
@ -1114,7 +1112,17 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* What area are we now in? */
ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy);
if (area == sad->sa1) {
if (sad->modifier == 1) {
/* Duplicate area into new window. */
WM_cursor_set(win, WM_CURSOR_EDIT);
is_gesture = (delta_max > area_threshold);
}
else if (sad->modifier == 2) {
/* Swap areas. */
WM_cursor_set(win, WM_CURSOR_SWAP_AREA);
is_gesture = true;
}
else if (area == sad->sa1) {
/* Same area, so possible split. */
WM_cursor_set(win,
SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT :
@ -1320,8 +1328,9 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event)
switch (event->type) {
case MOUSEMOVE:
/* second area, for join */
sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->xy);
/* Second area to swap with. */
sad->sa2 = ED_area_find_under_cursor(C, SPACE_TYPE_ANY, event->xy);
WM_cursor_set(CTX_wm_window(C), (sad->sa2) ? WM_CURSOR_SWAP_AREA : WM_CURSOR_STOP);
break;
case LEFTMOUSE: /* release LMB */
if (event->val == KM_RELEASE) {

View File

@ -29,7 +29,8 @@ set(SRC
paint_curve.c
paint_curve_undo.c
paint_hide.c
paint_image.c
paint_image.cc
paint_image_ops_paint.cc
paint_image_2d.c
paint_image_2d_curve_mask.cc
paint_image_proj.c

View File

@ -14,6 +14,10 @@
#include "curves_sculpt_intern.h"
#include "paint_intern.h"
/* -------------------------------------------------------------------- */
/** \name Poll Functions
* \{ */
bool CURVES_SCULPT_mode_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
@ -33,9 +37,11 @@ bool CURVES_SCULPT_mode_poll_view3d(bContext *C)
namespace blender::ed::sculpt_paint {
/* --------------------------------------------------------------------
* SCULPT_CURVES_OT_brush_stroke.
*/
/** \} */
/* -------------------------------------------------------------------- */
/** \name * SCULPT_CURVES_OT_brush_stroke
* \{ */
static bool stroke_get_location(bContext *C, float out[3], const float mouse[2])
{
@ -106,9 +112,11 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot)
paint_stroke_operator_properties(ot);
}
/* --------------------------------------------------------------------
* CURVES_OT_sculptmode_toggle.
*/
/** \} */
/* -------------------------------------------------------------------- */
/** \name * CURVES_OT_sculptmode_toggle
* \{ */
static bool curves_sculptmode_toggle_poll(bContext *C)
{
@ -177,9 +185,11 @@ static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
} // namespace blender::ed::sculpt_paint
/* --------------------------------------------------------------------
* Registration.
*/
/** \} */
/* -------------------------------------------------------------------- */
/** \name * Registration
* \{ */
void ED_operatortypes_sculpt_curves()
{
@ -187,3 +197,5 @@ void ED_operatortypes_sculpt_curves()
WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke);
WM_operatortype_append(CURVES_OT_sculptmode_toggle);
}
/** \} */

View File

@ -6,10 +6,10 @@
* \brief Functions to paint images in 2D and 3D.
*/
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <cfloat>
#include <cmath>
#include <cstdio>
#include <cstring>
#include "MEM_guardedalloc.h"
@ -67,6 +67,8 @@
#include "paint_intern.h"
extern "C" {
/**
* This is a static resource for non-global access.
* Maybe it should be exposed as part of the paint operation,
@ -96,7 +98,7 @@ void imapaint_region_tiles(
{
int srcx = 0, srcy = 0;
IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h);
IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h);
*tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS);
*th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS);
@ -107,11 +109,11 @@ void imapaint_region_tiles(
void ED_imapaint_dirty_region(
Image *ima, ImBuf *ibuf, ImageUser *iuser, int x, int y, int w, int h, bool find_old)
{
ImBuf *tmpibuf = NULL;
ImBuf *tmpibuf = nullptr;
int tilex, tiley, tilew, tileh, tx, ty;
int srcx = 0, srcy = 0;
IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h);
IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h);
if (w == 0 || h == 0) {
return;
@ -128,7 +130,7 @@ void ED_imapaint_dirty_region(
for (ty = tiley; ty <= tileh; ty++) {
for (tx = tilex; tx <= tilew; tx++) {
ED_image_paint_tile_push(
undo_tiles, ima, ibuf, &tmpibuf, iuser, tx, ty, NULL, NULL, false, find_old);
undo_tiles, ima, ibuf, &tmpibuf, iuser, tx, ty, nullptr, nullptr, false, find_old);
}
}
@ -169,17 +171,19 @@ void imapaint_image_update(
BlurKernel *paint_new_blur_kernel(Brush *br, bool proj)
{
int i, j;
BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel");
BlurKernel *kernel = MEM_new<BlurKernel>("BlurKernel");
float radius;
int side;
eBlurKernelType type = br->blur_mode;
eBlurKernelType type = static_cast<eBlurKernelType>(br->blur_mode);
if (proj) {
radius = 0.5f;
side = kernel->side = 2;
kernel->side_squared = kernel->side * kernel->side;
kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data");
kernel->wdata = static_cast<float *>(
MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"));
kernel->pixel_len = radius;
}
else {
@ -191,7 +195,8 @@ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj)
side = kernel->side = radius * 2 + 1;
kernel->side_squared = kernel->side * kernel->side;
kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data");
kernel->wdata = static_cast<float *>(
MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"));
kernel->pixel_len = br->blur_kernel_radius;
}
@ -224,9 +229,9 @@ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj)
default:
printf("unidentified kernel type, aborting\n");
MEM_freeN(kernel->wdata);
MEM_freeN(kernel);
return NULL;
paint_delete_blur_kernel(kernel);
MEM_delete(kernel);
return nullptr;
}
return kernel;
@ -267,7 +272,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool)
SpaceImage *sima = CTX_wm_space_image(C);
if (sima) {
if (sima->image != NULL && ID_IS_LINKED(sima->image)) {
if (sima->image != nullptr && ID_IS_LINKED(sima->image)) {
return false;
}
ARegion *region = CTX_wm_region(C);
@ -281,7 +286,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool)
return false;
}
static bool image_paint_poll(bContext *C)
bool image_paint_poll(bContext *C)
{
return image_paint_poll_ex(C, true);
}
@ -307,24 +312,6 @@ static bool image_paint_2d_clone_poll(bContext *C)
}
/************************ paint operator ************************/
typedef enum eTexPaintMode {
PAINT_MODE_2D,
PAINT_MODE_3D_PROJECT,
} eTexPaintMode;
typedef struct PaintOperation {
eTexPaintMode mode;
void *custom_paint;
float prevmouse[2];
float startmouse[2];
double starttime;
void *cursor;
ViewContext vc;
} PaintOperation;
bool paint_use_opacity_masking(Brush *brush)
{
return ((brush->flag & BRUSH_AIRBRUSH) || (brush->flag & BRUSH_DRAG_DOT) ||
@ -414,370 +401,11 @@ void paint_brush_exit_tex(Brush *brush)
}
}
static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata)
{
PaintOperation *pop = (PaintOperation *)customdata;
if (pop) {
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
ARegion *region = pop->vc.region;
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_line_width(4.0);
immUniformColor4ub(0, 0, 0, 255);
immBegin(GPU_PRIM_LINES, 2);
immVertex2i(pos, x, y);
immVertex2i(
pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
immEnd();
GPU_line_width(2.0);
immUniformColor4ub(255, 255, 255, 255);
immBegin(GPU_PRIM_LINES, 2);
immVertex2i(pos, x, y);
immVertex2i(
pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
immEnd();
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
}
static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2])
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *settings = scene->toolsettings;
PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */
Brush *brush = BKE_paint_brush(&settings->imapaint.paint);
int mode = RNA_enum_get(op->ptr, "mode");
ED_view3d_viewcontext_init(C, &pop->vc, depsgraph);
copy_v2_v2(pop->prevmouse, mouse);
copy_v2_v2(pop->startmouse, mouse);
/* initialize from context */
if (CTX_wm_region_view3d(C)) {
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = OBACT(view_layer);
bool uvs, mat, tex, stencil;
if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) {
ED_paint_data_warning(op->reports, uvs, mat, tex, stencil);
MEM_freeN(pop);
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
return NULL;
}
pop->mode = PAINT_MODE_3D_PROJECT;
pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode);
}
else {
pop->mode = PAINT_MODE_2D;
pop->custom_paint = paint_2d_new_stroke(C, op, mode);
}
if (!pop->custom_paint) {
MEM_freeN(pop);
return NULL;
}
if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) {
pop->cursor = WM_paint_cursor_activate(
SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop);
}
settings->imapaint.flag |= IMAGEPAINT_DRAWING;
ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
return pop;
}
static void paint_stroke_update_step(bContext *C,
wmOperator *UNUSED(op),
struct PaintStroke *stroke,
PointerRNA *itemptr)
{
PaintOperation *pop = paint_stroke_mode_data(stroke);
Scene *scene = CTX_data_scene(C);
ToolSettings *toolsettings = CTX_data_tool_settings(C);
UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f;
/* initial brush values. Maybe it should be considered moving these to stroke system */
float startalpha = BKE_brush_alpha_get(scene, brush);
float mouse[2];
float pressure;
float size;
float distance = paint_stroke_distance_get(stroke);
int eraser;
RNA_float_get_array(itemptr, "mouse", mouse);
pressure = RNA_float_get(itemptr, "pressure");
eraser = RNA_boolean_get(itemptr, "pen_flip");
size = RNA_float_get(itemptr, "size");
/* stroking with fill tool only acts on stroke end */
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
copy_v2_v2(pop->prevmouse, mouse);
return;
}
if (BKE_brush_use_alpha_pressure(brush)) {
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac));
}
else {
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
}
if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
ED_image_undo_restore(ustack->step_init);
}
switch (pop->mode) {
case PAINT_MODE_2D:
paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size);
break;
case PAINT_MODE_3D_PROJECT:
paint_proj_stroke(
C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size);
break;
}
copy_v2_v2(pop->prevmouse, mouse);
/* restore brush values */
BKE_brush_alpha_set(scene, brush, startalpha);
}
static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final)
{
PaintOperation *pop = paint_stroke_mode_data(stroke);
switch (pop->mode) {
case PAINT_MODE_2D:
paint_2d_redraw(C, pop->custom_paint, final);
break;
case PAINT_MODE_3D_PROJECT:
paint_proj_redraw(C, pop->custom_paint, final);
break;
}
}
static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *toolsettings = scene->toolsettings;
PaintOperation *pop = paint_stroke_mode_data(stroke);
Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
if (brush->flag & BRUSH_USE_GRADIENT) {
switch (pop->mode) {
case PAINT_MODE_2D:
paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
break;
case PAINT_MODE_3D_PROJECT:
paint_proj_stroke(C,
pop->custom_paint,
pop->startmouse,
pop->prevmouse,
paint_stroke_flipped(stroke),
1.0,
0.0,
BKE_brush_size_get(scene, brush));
/* two redraws, one for GPU update, one for notification */
paint_proj_redraw(C, pop->custom_paint, false);
paint_proj_redraw(C, pop->custom_paint, true);
break;
}
}
else {
switch (pop->mode) {
case PAINT_MODE_2D:
float color[3];
if (paint_stroke_inverted(stroke)) {
srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush));
}
else {
srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
}
paint_2d_bucket_fill(
C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
break;
case PAINT_MODE_3D_PROJECT:
paint_proj_stroke(C,
pop->custom_paint,
pop->startmouse,
pop->prevmouse,
paint_stroke_flipped(stroke),
1.0,
0.0,
BKE_brush_size_get(scene, brush));
/* two redraws, one for GPU update, one for notification */
paint_proj_redraw(C, pop->custom_paint, false);
paint_proj_redraw(C, pop->custom_paint, true);
break;
}
}
}
switch (pop->mode) {
case PAINT_MODE_2D:
paint_2d_stroke_done(pop->custom_paint);
break;
case PAINT_MODE_3D_PROJECT:
paint_proj_stroke_done(pop->custom_paint);
break;
}
if (pop->cursor) {
WM_paint_cursor_end(pop->cursor);
}
ED_image_undo_push_end();
/* duplicate warning, see texpaint_init */
#if 0
if (pop->s.warnmultifile) {
BKE_reportf(op->reports,
RPT_WARNING,
"Image requires 4 color channels to paint: %s",
pop->s.warnmultifile);
}
if (pop->s.warnpackedfile) {
BKE_reportf(op->reports,
RPT_WARNING,
"Packed MultiLayer files cannot be painted: %s",
pop->s.warnpackedfile);
}
#endif
MEM_freeN(pop);
}
static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
{
PaintOperation *pop;
/* TODO: Should avoid putting this here. Instead, last position should be requested
* from stroke system. */
if (!(pop = texture_paint_init(C, op, mouse))) {
return false;
}
paint_stroke_set_mode_data(op->customdata, pop);
return true;
}
static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
op->customdata = paint_stroke_new(C,
op,
NULL,
paint_stroke_test_start,
paint_stroke_update_step,
paint_stroke_redraw,
paint_stroke_done,
event->type);
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
paint_stroke_free(C, op, op->customdata);
return OPERATOR_FINISHED;
}
/* add modal handler */
WM_event_add_modal_handler(C, op);
OPERATOR_RETVAL_CHECK(retval);
BLI_assert(retval == OPERATOR_RUNNING_MODAL);
return OPERATOR_RUNNING_MODAL;
}
static int paint_exec(bContext *C, wmOperator *op)
{
PropertyRNA *strokeprop;
PointerRNA firstpoint;
float mouse[2];
strokeprop = RNA_struct_find_property(op->ptr, "stroke");
if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
return OPERATOR_CANCELLED;
}
RNA_float_get_array(&firstpoint, "mouse", mouse);
op->customdata = paint_stroke_new(C,
op,
NULL,
paint_stroke_test_start,
paint_stroke_update_step,
paint_stroke_redraw,
paint_stroke_done,
0);
/* frees op->customdata */
return paint_stroke_exec(C, op, op->customdata);
}
static int paint_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
return paint_stroke_modal(C, op, event, op->customdata);
}
static void paint_cancel(bContext *C, wmOperator *op)
{
paint_stroke_cancel(C, op, op->customdata);
}
void PAINT_OT_image_paint(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Image Paint";
ot->idname = "PAINT_OT_image_paint";
ot->description = "Paint a stroke into the image";
/* api callbacks */
ot->invoke = paint_invoke;
ot->modal = paint_modal;
ot->exec = paint_exec;
ot->poll = image_paint_poll;
ot->cancel = paint_cancel;
/* flags */
ot->flag = OPTYPE_BLOCKING;
paint_stroke_operator_properties(ot);
}
bool get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
{
ScrArea *area = CTX_wm_area(C);
if (area && area->spacetype == SPACE_IMAGE) {
SpaceImage *sima = area->spacedata.first;
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
if (sima->mode == SI_MODE_PAINT) {
ARegion *region = CTX_wm_region(C);
ED_space_image_get_zoom(sima, region, zoomx, zoomy);
@ -798,8 +426,8 @@ static void toggle_paint_cursor(Scene *scene, bool enable)
Paint *p = &settings->imapaint.paint;
if (p->paint_cursor && !enable) {
WM_paint_cursor_end(p->paint_cursor);
p->paint_cursor = NULL;
WM_paint_cursor_end(static_cast<wmPaintCursor *>(p->paint_cursor));
p->paint_cursor = nullptr;
paint_cursor_delete_textures();
}
else if (enable) {
@ -837,10 +465,10 @@ void ED_space_image_paint_update(Main *bmain, wmWindowManager *wm, Scene *scene)
/************************ grab clone operator ************************/
typedef struct GrabClone {
struct GrabClone {
float startoffset[2];
int startx, starty;
} GrabClone;
};
static void grab_clone_apply(bContext *C, wmOperator *op)
{
@ -864,7 +492,7 @@ static int grab_clone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Brush *brush = image_paint_brush(C);
GrabClone *cmv;
cmv = MEM_callocN(sizeof(GrabClone), "GrabClone");
cmv = MEM_new<GrabClone>("GrabClone");
copy_v2_v2(cmv->startoffset, brush->clone.offset);
cmv->startx = event->xy[0];
cmv->starty = event->xy[1];
@ -879,7 +507,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Brush *brush = image_paint_brush(C);
ARegion *region = CTX_wm_region(C);
GrabClone *cmv = op->customdata;
GrabClone *cmv = static_cast<GrabClone *>(op->customdata);
float startfx, startfy, fx, fy, delta[2];
int xmin = region->winrct.xmin, ymin = region->winrct.ymin;
@ -910,7 +538,8 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void grab_clone_cancel(bContext *UNUSED(C), wmOperator *op)
{
MEM_freeN(op->customdata);
GrabClone *cmv = static_cast<GrabClone *>(op->customdata);
MEM_delete(cmv);
}
void PAINT_OT_grab_clone(wmOperatorType *ot)
@ -934,7 +563,7 @@ void PAINT_OT_grab_clone(wmOperatorType *ot)
RNA_def_float_vector(ot->srna,
"delta",
2,
NULL,
nullptr,
-FLT_MAX,
FLT_MAX,
"Delta",
@ -944,12 +573,12 @@ void PAINT_OT_grab_clone(wmOperatorType *ot)
}
/******************** sample color operator ********************/
typedef struct {
struct SampleColorData {
bool show_cursor;
short launch_event;
float initcolor[3];
bool sample_palette;
} SampleColorData;
};
static void sample_color_update_header(SampleColorData *data, bContext *C)
{
@ -1003,7 +632,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event
Scene *scene = CTX_data_scene(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data");
SampleColorData *data = MEM_new<SampleColorData>("sample color custom data");
ARegion *region = CTX_wm_region(C);
wmWindow *win = CTX_wm_window(C);
@ -1039,7 +668,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event
static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
SampleColorData *data = op->customdata;
SampleColorData *data = static_cast<SampleColorData *>(op->customdata);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
@ -1053,8 +682,8 @@ static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event)
RNA_boolean_set(op->ptr, "palette", true);
}
WM_cursor_modal_restore(CTX_wm_window(C));
MEM_freeN(data);
ED_workspace_status_text(C, NULL);
MEM_delete(data);
ED_workspace_status_text(C, nullptr);
return OPERATOR_FINISHED;
}
@ -1113,25 +742,26 @@ void PAINT_OT_sample_color(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
prop = RNA_def_int_vector(
ot->srna, "location", 2, nullptr, 0, INT_MAX, "Location", "", 0, 16384);
RNA_def_property_flag(prop, static_cast<PropertyFlag>(PROP_SKIP_SAVE | PROP_HIDDEN));
RNA_def_boolean(ot->srna, "merged", 0, "Sample Merged", "Sample the output display color");
RNA_def_boolean(ot->srna, "palette", 0, "Add to Palette", "");
RNA_def_boolean(ot->srna, "merged", false, "Sample Merged", "Sample the output display color");
RNA_def_boolean(ot->srna, "palette", false, "Add to Palette", "");
}
/******************** texture paint toggle operator ********************/
void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob)
{
Image *ima = NULL;
Image *ima = nullptr;
ImagePaintSettings *imapaint = &scene->toolsettings->imapaint;
/* This has to stay here to regenerate the texture paint
* cache in case we are loading a file */
BKE_texpaint_slots_refresh_object(scene, ob);
ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr);
/* entering paint mode also sets image to editors */
if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) {
@ -1147,11 +777,11 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob
}
if (ima) {
wmWindowManager *wm = bmain->wm.first;
for (wmWindow *win = wm->windows.first; win; win = win->next) {
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
const bScreen *screen = WM_window_get_active_screen(win);
for (ScrArea *area = screen->areabase.first; area; area = area->next) {
SpaceLink *sl = area->spacedata.first;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
if (sl->spacetype == SPACE_IMAGE) {
SpaceImage *sima = (SpaceImage *)sl;
@ -1172,12 +802,12 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob
if (U.glreslimit != 0) {
BKE_image_free_all_gputextures(bmain);
}
BKE_image_paint_set_mipmap(bmain, 0);
BKE_image_paint_set_mipmap(bmain, false);
toggle_paint_cursor(scene, true);
Mesh *me = BKE_mesh_from_object(ob);
BLI_assert(me != NULL);
BLI_assert(me != nullptr);
DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_SCENE | ND_MODE, scene);
}
@ -1197,11 +827,11 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob)
if (U.glreslimit != 0) {
BKE_image_free_all_gputextures(bmain);
}
BKE_image_paint_set_mipmap(bmain, 1);
BKE_image_paint_set_mipmap(bmain, true);
toggle_paint_cursor(scene, false);
Mesh *me = BKE_mesh_from_object(ob);
BLI_assert(me != NULL);
BLI_assert(me != nullptr);
DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_SCENE | ND_MODE, scene);
}
@ -1217,7 +847,7 @@ void ED_object_texture_paint_mode_exit(bContext *C)
static bool texture_paint_toggle_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
if (ob == NULL || ob->type != OB_MESH) {
if (ob == nullptr || ob->type != OB_MESH) {
return false;
}
if (!ob->data || ID_IS_LINKED(ob->data)) {
@ -1237,7 +867,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op)
const bool is_mode_set = (ob->mode & mode_flag) != 0;
if (!is_mode_set) {
if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
if (!ED_object_mode_compat_set(C, ob, static_cast<eObjectMode>(mode_flag), op->reports)) {
return OPERATOR_CANCELLED;
}
}
@ -1304,7 +934,7 @@ static bool brush_colors_flip_poll(bContext *C)
}
else {
Object *ob = CTX_data_active_object(C);
if (ob != NULL) {
if (ob != nullptr) {
if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT | OB_MODE_SCULPT)) {
return true;
}
@ -1340,8 +970,8 @@ void ED_imapaint_bucket_fill(struct bContext *C,
ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
const float mouse_init[2] = {mouse[0], mouse[1]};
paint_2d_bucket_fill(C, color, NULL, mouse_init, NULL, NULL);
const float mouse_init[2] = {static_cast<float>(mouse[0]), static_cast<float>(mouse[1])};
paint_2d_bucket_fill(C, color, nullptr, mouse_init, nullptr, nullptr);
ED_image_undo_push_end();
@ -1379,3 +1009,4 @@ bool mask_paint_poll(bContext *C)
{
return BKE_paint_select_elem_test(CTX_data_active_object(C));
}
}

View File

@ -0,0 +1,530 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup edsculpt
* \brief Painting operator to paint in 2D and 3D.
*/
#include "DNA_brush_types.h"
#include "DNA_color_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_paint.h"
#include "BKE_undo_system.h"
#include "DEG_depsgraph.h"
#include "ED_paint.h"
#include "ED_view3d.h"
#include "GPU_immediate.h"
#include "GPU_state.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
#include "WM_types.h"
#include "paint_intern.h"
namespace blender::ed::sculpt_paint::image::ops::paint {
/**
* Interface to use the same painting operator for 3D and 2D painting. Interface removes the
* differences between the actual calls that are being performed.
*/
class AbstractPaintMode {
public:
virtual void *paint_new_stroke(
bContext *C, wmOperator *op, Object *ob, const float mouse[2], int mode) = 0;
virtual void paint_stroke(bContext *C,
void *stroke_handle,
float prev_mouse[2],
float mouse[2],
int eraser,
float pressure,
float distance,
float size) = 0;
virtual void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) = 0;
virtual void paint_stroke_done(void *stroke_handle) = 0;
virtual void paint_gradient_fill(const bContext *C,
const Scene *scene,
Brush *brush,
struct PaintStroke *stroke,
void *stroke_handle,
float mouse_start[2],
float mouse_end[2]) = 0;
virtual void paint_bucket_fill(const bContext *C,
const Scene *scene,
Brush *brush,
struct PaintStroke *stroke,
void *stroke_handle,
float mouse_start[2],
float mouse_end[2]) = 0;
};
class ImagePaintMode : public AbstractPaintMode {
public:
void *paint_new_stroke(bContext *C,
wmOperator *op,
Object *UNUSED(ob),
const float UNUSED(mouse[2]),
int mode) override
{
return paint_2d_new_stroke(C, op, mode);
}
void paint_stroke(bContext *UNUSED(C),
void *stroke_handle,
float prev_mouse[2],
float mouse[2],
int eraser,
float pressure,
float distance,
float size) override
{
paint_2d_stroke(stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size);
}
void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override
{
paint_2d_redraw(C, stroke_handle, final);
}
void paint_stroke_done(void *stroke_handle) override
{
paint_2d_stroke_done(stroke_handle);
}
void paint_gradient_fill(const bContext *C,
const Scene *UNUSED(scene),
Brush *brush,
struct PaintStroke *UNUSED(stroke),
void *stroke_handle,
float mouse_start[2],
float mouse_end[2]) override
{
paint_2d_gradient_fill(C, brush, mouse_start, mouse_end, stroke_handle);
}
void paint_bucket_fill(const bContext *C,
const Scene *scene,
Brush *brush,
struct PaintStroke *stroke,
void *stroke_handle,
float mouse_start[2],
float mouse_end[2]) override
{
float color[3];
if (paint_stroke_inverted(stroke)) {
srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush));
}
else {
srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
}
paint_2d_bucket_fill(C, color, brush, mouse_start, mouse_end, stroke_handle);
}
};
class ProjectionPaintMode : public AbstractPaintMode {
public:
void *paint_new_stroke(
bContext *C, wmOperator *UNUSED(op), Object *ob, const float mouse[2], int mode) override
{
return paint_proj_new_stroke(C, ob, mouse, mode);
}
void paint_stroke(bContext *C,
void *stroke_handle,
float prev_mouse[2],
float mouse[2],
int eraser,
float pressure,
float distance,
float size) override
{
paint_proj_stroke(C, stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size);
};
void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override
{
paint_proj_redraw(C, stroke_handle, final);
}
void paint_stroke_done(void *stroke_handle) override
{
paint_proj_stroke_done(stroke_handle);
}
void paint_gradient_fill(const bContext *C,
const Scene *scene,
Brush *brush,
struct PaintStroke *stroke,
void *stroke_handle,
float mouse_start[2],
float mouse_end[2]) override
{
paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end);
}
void paint_bucket_fill(const bContext *C,
const Scene *scene,
Brush *brush,
struct PaintStroke *stroke,
void *stroke_handle,
float mouse_start[2],
float mouse_end[2]) override
{
paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end);
}
private:
void paint_fill(const bContext *C,
const Scene *scene,
Brush *brush,
struct PaintStroke *stroke,
void *stroke_handle,
float mouse_start[2],
float mouse_end[2])
{
paint_proj_stroke(C,
stroke_handle,
mouse_start,
mouse_end,
paint_stroke_flipped(stroke),
1.0,
0.0,
BKE_brush_size_get(scene, brush));
/* two redraws, one for GPU update, one for notification */
paint_proj_redraw(C, stroke_handle, false);
paint_proj_redraw(C, stroke_handle, true);
}
};
struct PaintOperation {
AbstractPaintMode *mode = nullptr;
void *stroke_handle = nullptr;
float prevmouse[2] = {0.0f, 0.0f};
float startmouse[2] = {0.0f, 0.0f};
double starttime = 0.0;
wmPaintCursor *cursor = nullptr;
ViewContext vc = {nullptr};
PaintOperation() = default;
~PaintOperation()
{
MEM_delete(mode);
mode = nullptr;
if (cursor) {
WM_paint_cursor_end(cursor);
cursor = nullptr;
}
}
};
static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata)
{
PaintOperation *pop = (PaintOperation *)customdata;
if (pop) {
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
ARegion *region = pop->vc.region;
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
GPU_line_width(4.0);
immUniformColor4ub(0, 0, 0, 255);
immBegin(GPU_PRIM_LINES, 2);
immVertex2i(pos, x, y);
immVertex2i(
pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
immEnd();
GPU_line_width(2.0);
immUniformColor4ub(255, 255, 255, 255);
immBegin(GPU_PRIM_LINES, 2);
immVertex2i(pos, x, y);
immVertex2i(
pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin);
immEnd();
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
}
}
static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2])
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *settings = scene->toolsettings;
PaintOperation *pop = MEM_new<PaintOperation>("PaintOperation"); /* caller frees */
Brush *brush = BKE_paint_brush(&settings->imapaint.paint);
int mode = RNA_enum_get(op->ptr, "mode");
ED_view3d_viewcontext_init(C, &pop->vc, depsgraph);
copy_v2_v2(pop->prevmouse, mouse);
copy_v2_v2(pop->startmouse, mouse);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = OBACT(view_layer);
/* initialize from context */
if (CTX_wm_region_view3d(C)) {
bool uvs, mat, tex, stencil;
if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) {
ED_paint_data_warning(op->reports, uvs, mat, tex, stencil);
MEM_delete(pop);
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr);
return nullptr;
}
pop->mode = MEM_new<ProjectionPaintMode>("ProjectionPaintMode");
}
else {
pop->mode = MEM_new<ImagePaintMode>("ImagePaintMode");
}
pop->stroke_handle = pop->mode->paint_new_stroke(C, op, ob, mouse, mode);
if (!pop->stroke_handle) {
MEM_delete(pop);
return nullptr;
}
if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) {
pop->cursor = WM_paint_cursor_activate(
SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop);
}
settings->imapaint.flag |= IMAGEPAINT_DRAWING;
ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D);
return pop;
}
static void paint_stroke_update_step(bContext *C,
wmOperator *UNUSED(op),
struct PaintStroke *stroke,
PointerRNA *itemptr)
{
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
Scene *scene = CTX_data_scene(C);
ToolSettings *toolsettings = CTX_data_tool_settings(C);
UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings;
Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f;
/* initial brush values. Maybe it should be considered moving these to stroke system */
float startalpha = BKE_brush_alpha_get(scene, brush);
float mouse[2];
float pressure;
float size;
float distance = paint_stroke_distance_get(stroke);
int eraser;
RNA_float_get_array(itemptr, "mouse", mouse);
pressure = RNA_float_get(itemptr, "pressure");
eraser = RNA_boolean_get(itemptr, "pen_flip");
size = RNA_float_get(itemptr, "size");
/* stroking with fill tool only acts on stroke end */
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
copy_v2_v2(pop->prevmouse, mouse);
return;
}
if (BKE_brush_use_alpha_pressure(brush)) {
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac));
}
else {
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
}
if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
ED_image_undo_restore(ustack->step_init);
}
pop->mode->paint_stroke(
C, pop->stroke_handle, pop->prevmouse, mouse, eraser, pressure, distance, size);
copy_v2_v2(pop->prevmouse, mouse);
/* restore brush values */
BKE_brush_alpha_set(scene, brush, startalpha);
}
static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final)
{
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
pop->mode->paint_stroke_redraw(C, pop->stroke_handle, final);
}
static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
{
Scene *scene = CTX_data_scene(C);
ToolSettings *toolsettings = scene->toolsettings;
PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke));
Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint);
toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING;
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
if (brush->flag & BRUSH_USE_GRADIENT) {
pop->mode->paint_gradient_fill(
C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse);
}
else {
pop->mode->paint_bucket_fill(
C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse);
}
}
pop->mode->paint_stroke_done(pop->stroke_handle);
pop->stroke_handle = nullptr;
ED_image_undo_push_end();
/* duplicate warning, see texpaint_init */
#if 0
if (pop->s.warnmultifile) {
BKE_reportf(op->reports,
RPT_WARNING,
"Image requires 4 color channels to paint: %s",
pop->s.warnmultifile);
}
if (pop->s.warnpackedfile) {
BKE_reportf(op->reports,
RPT_WARNING,
"Packed MultiLayer files cannot be painted: %s",
pop->s.warnpackedfile);
}
#endif
MEM_delete(pop);
}
static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2])
{
PaintOperation *pop;
/* TODO: Should avoid putting this here. Instead, last position should be requested
* from stroke system. */
if (!(pop = texture_paint_init(C, op, mouse))) {
return false;
}
paint_stroke_set_mode_data(static_cast<PaintStroke *>(op->customdata), pop);
return true;
}
static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
op->customdata = paint_stroke_new(C,
op,
nullptr,
paint_stroke_test_start,
paint_stroke_update_step,
paint_stroke_redraw,
paint_stroke_done,
event->type);
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
paint_stroke_free(C, op, static_cast<PaintStroke *>(op->customdata));
return OPERATOR_FINISHED;
}
/* add modal handler */
WM_event_add_modal_handler(C, op);
OPERATOR_RETVAL_CHECK(retval);
BLI_assert(retval == OPERATOR_RUNNING_MODAL);
return OPERATOR_RUNNING_MODAL;
}
static int paint_exec(bContext *C, wmOperator *op)
{
PropertyRNA *strokeprop;
PointerRNA firstpoint;
float mouse[2];
strokeprop = RNA_struct_find_property(op->ptr, "stroke");
if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) {
return OPERATOR_CANCELLED;
}
RNA_float_get_array(&firstpoint, "mouse", mouse);
op->customdata = paint_stroke_new(C,
op,
nullptr,
paint_stroke_test_start,
paint_stroke_update_step,
paint_stroke_redraw,
paint_stroke_done,
0);
/* frees op->customdata */
return paint_stroke_exec(C, op, static_cast<PaintStroke *>(op->customdata));
}
static int paint_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
return paint_stroke_modal(C, op, event, static_cast<PaintStroke *>(op->customdata));
}
static void paint_cancel(bContext *C, wmOperator *op)
{
paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
}
} // namespace blender::ed::sculpt_paint::image::ops::paint
extern "C" {
void PAINT_OT_image_paint(wmOperatorType *ot)
{
using namespace blender::ed::sculpt_paint::image::ops::paint;
/* identifiers */
ot->name = "Image Paint";
ot->idname = "PAINT_OT_image_paint";
ot->description = "Paint a stroke into the image";
/* api callbacks */
ot->invoke = paint_invoke;
ot->modal = paint_modal;
ot->exec = paint_exec;
ot->poll = image_paint_poll;
ot->cancel = paint_cancel;
/* flags */
ot->flag = OPTYPE_BLOCKING;
paint_stroke_operator_properties(ot);
}
}

View File

@ -269,6 +269,7 @@ void paint_brush_color_get(struct Scene *scene,
bool paint_use_opacity_masking(struct Brush *brush);
void paint_brush_init_tex(struct Brush *brush);
void paint_brush_exit_tex(struct Brush *brush);
bool image_paint_poll(struct bContext *C);
void PAINT_OT_grab_clone(struct wmOperatorType *ot);
void PAINT_OT_sample_color(struct wmOperatorType *ot);

View File

@ -456,7 +456,18 @@ void CONSOLE_OT_insert(wmOperatorType *ot)
static int console_indent_or_autocomplete_exec(bContext *C, wmOperator *UNUSED(op))
{
ConsoleLine *ci = console_history_verify(C);
bool text_before_cursor = ci->cursor != 0 && !ELEM(ci->line[ci->cursor - 1], ' ', '\t');
bool text_before_cursor = false;
/* Check any text before cursor (not just the previous character) as is done for
* #TEXT_OT_indent_or_autocomplete because Python auto-complete operates on import
* statements such as completing possible sub-modules: `from bpy import `. */
for (int i = 0; i < ci->cursor; i += BLI_str_utf8_size_safe(&ci->line[i])) {
if (!ELEM(ci->line[i], ' ', '\t')) {
text_before_cursor = true;
break;
}
}
if (text_before_cursor) {
WM_operator_name_call(C, "CONSOLE_OT_autocomplete", WM_OP_INVOKE_DEFAULT, NULL);
}

View File

@ -242,7 +242,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
GPU_blend(GPU_BLEND_NONE);
/* Vertical component of of the cursor. */
/* Vertical component of the cursor. */
if (sipo->mode == SIPO_MODE_DRIVERS) {
/* cursor x-value */
float x = sipo->cursorTime;

View File

@ -147,7 +147,7 @@ static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerR
} // namespace blender::ed::space_node
#define SAMPLE_FLT_ISNONE FLT_MAX
/* Bad bad, 2.5 will do better? ... no it won't! */
/* Bad! 2.5 will do better? ... no it won't! */
static float _sample_col[4] = {SAMPLE_FLT_ISNONE};
void ED_node_sample_set(const float col[4])
{

View File

@ -1049,7 +1049,7 @@ static void node_socket_draw_nested(const bContext &C,
},
data,
MEM_freeN);
/* Disable the button so that clicks on it are ignored the the link operator still works. */
/* Disable the button so that clicks on it are ignored the link operator still works. */
UI_but_flag_enable(but, UI_BUT_DISABLED);
UI_block_emboss_set(&block, old_emboss);
}

View File

@ -1636,7 +1636,9 @@ static void sequencer_draw_gpencil_overlay(const bContext *C)
ED_annotation_draw_view2d(C, 0);
}
/* Draw content and safety borders borders. */
/**
* Draw content and safety borders.
*/
static void sequencer_draw_borders_overlay(const SpaceSeq *sseq,
const View2D *v2d,
const Scene *scene)

View File

@ -26,7 +26,7 @@ struct FieldTreeInfo {
*/
MultiValueMap<GFieldRef, GFieldRef> field_users;
/**
* The same field input may exist in the field tree as as separate nodes due to the way
* The same field input may exist in the field tree as separate nodes due to the way
* the tree is constructed. This set contains every different input only once.
*/
VectorSet<std::reference_wrapper<const FieldInput>> deduplicated_field_inputs;
@ -137,7 +137,7 @@ static Set<GFieldRef> find_varying_fields(const FieldTreeInfo &field_tree_info,
}
/**
* Builds the #procedure so that it computes the the fields.
* Builds the #procedure so that it computes the fields.
*/
static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
ResourceScope &scope,

View File

@ -147,6 +147,7 @@ set(SRC
intern/gpu_select_private.h
intern/gpu_shader_create_info.hh
intern/gpu_shader_create_info_private.hh
intern/gpu_shader_dependency_private.h
intern/gpu_shader_interface.hh
intern/gpu_shader_private.hh
intern/gpu_state_private.hh
@ -384,7 +385,7 @@ file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}")
list(APPEND SRC ${glsl_source_list_file})
list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
set(SHADER_CREATE_INFOS
set(SRC_SHADER_CREATE_INFOS
../draw/engines/workbench/shaders/infos/workbench_composite_info.hh
../draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh
../draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh
@ -435,7 +436,7 @@ set(SHADER_CREATE_INFOS
)
set(SHADER_CREATE_INFOS_CONTENT "")
foreach(DESCRIPTOR_FILE ${SHADER_CREATE_INFOS})
foreach(DESCRIPTOR_FILE ${SRC_SHADER_CREATE_INFOS})
string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n")
endforeach()
@ -486,18 +487,22 @@ if(WITH_GPU_SHADER_BUILDER)
)
target_include_directories(shader_builder PRIVATE ${INC} ${CMAKE_CURRENT_BINARY_DIR})
set(BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh)
set(SRC_BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh)
add_custom_command(
OUTPUT
${BAKED_CREATE_INFOS_FILE}
${SRC_BAKED_CREATE_INFOS_FILE}
COMMAND
"$<TARGET_FILE:shader_builder>" ${BAKED_CREATE_INFOS_FILE}
"$<TARGET_FILE:shader_builder>" ${SRC_BAKED_CREATE_INFOS_FILE}
DEPENDS shader_builder
)
set(GPU_SHADER_INFO_SRC
intern/gpu_shader_info_baked.cc
${BAKED_CREATE_INFOS_FILE}
${SRC_BAKED_CREATE_INFOS_FILE}
# For project files to be aware of these headers.
${SRC_SHADER_CREATE_INFOS}
shaders/infos/gpu_interface_info.hh
)
blender_add_lib(bf_gpu_shader_infos "${GPU_SHADER_INFO_SRC}" "" "" "")

View File

@ -119,6 +119,7 @@ int GPU_batch_instbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo);
* Returns the index of verts in the batch.
*/
int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo);
bool GPU_batch_vertbuf_has(GPUBatch *, GPUVertBuf *);
#define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false)

View File

@ -191,6 +191,16 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo)
return -1;
}
bool GPU_batch_vertbuf_has(GPUBatch *batch, GPUVertBuf *verts)
{
for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
if (batch->verts[v] == verts) {
return true;
}
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -497,9 +497,9 @@ struct ShaderCreateInfo {
/**
* IMPORTANT: invocations count is only used if GL_ARB_gpu_shader5 is supported. On
* implementations that do not supports it, the max_vertices will be be multiplied by
* invocations. Your shader needs to account for this fact. Use `#ifdef GPU_ARB_gpu_shader5`
* and make a code path that does not rely on gl_InvocationID.
* implementations that do not supports it, the max_vertices will be multiplied by invocations.
* Your shader needs to account for this fact. Use `#ifdef GPU_ARB_gpu_shader5` and make a code
* path that does not rely on #gl_InvocationID.
*/
Self &geometry_layout(PrimitiveIn prim_in,
PrimitiveOut prim_out,

View File

@ -38,7 +38,7 @@ StringRefNull gpu_shader_dependency_get_source(const StringRefNull source_name);
/**
* \brief Find the name of the file from which the given string was generated.
* \return Return filename or empty string.
* \return filename or empty string.
* \note source_string needs to be identical to the one given by gpu_shader_dependency_get_source()
*/
StringRefNull gpu_shader_dependency_get_filename_from_source_string(

View File

@ -139,7 +139,7 @@ void GLImmediate::end()
GLContext::get()->state_manager->apply_state();
/* We convert the offset in vertex offset from the buffer's start.
* This works because we added some padding to align the first vertex vertex. */
* This works because we added some padding to align the first vertex. */
uint v_first = buffer_offset() / vertex_format.stride;
GLVertArray::update_bindings(
vao_id_, v_first, &vertex_format, reinterpret_cast<Shader *>(shader)->interface);

View File

@ -244,9 +244,9 @@ static int initialize_chain(Object *ob, bPoseChannel *pchan_tip, bConstraint *co
}
if (BLI_listbase_is_empty(&curchan->iktree) == false) {
/* Oh oh, there is already a chain starting from this channel and our chain is longer...
/* Oh, there is already a chain starting from this channel and our chain is longer.
* Should handle this by moving the previous chain up to the beginning of our chain
* For now we just stop here */
* For now we just stop here. */
break;
}
}

View File

@ -1162,7 +1162,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte
}
/* Assess scrubbing performance of provided file. This function is not meant to be very exact.
* It compares number number of frames decoded in reasonable time with largest detected GOP size.
* It compares number of frames decoded in reasonable time with largest detected GOP size.
* Because seeking happens in single GOP, it means, that maximum seek time can be detected this
* way.
* Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or

View File

@ -98,6 +98,10 @@ typedef enum CustomDataType {
CD_MTFACE = 5,
CD_MCOL = 6,
CD_ORIGINDEX = 7,
/**
* Used for derived face corner normals on mesh `ldata`, since currently they are not computed
* lazily. Derived vertex and polygon normals are stored in #Mesh_Runtime.
*/
CD_NORMAL = 8,
CD_FACEMAP = 9, /* exclusive face group, each face can only be part of one */
CD_PROP_FLOAT = 10,

View File

@ -611,7 +611,7 @@ typedef struct FluidDomainSettings {
/* Fluid guiding options. */
float guide_alpha; /* Guiding weight scalar (determines strength). */
int guide_beta; /* Guiding blur radius (affects size of vortices vortices). */
int guide_beta; /* Guiding blur radius (affects size of vortices). */
float guide_vel_factor; /* Multiply guiding velocity by this factor. */
int guide_res[3]; /* Res for velocity guide grids - independent from base res. */
short guide_source;

View File

@ -116,27 +116,25 @@ typedef struct Mesh_Runtime {
*/
char wrapper_type_finalize;
int subsurf_resolution;
/**
* Settings for lazily evaluating the subdivision on the CPU if needed. These are
* set in the modifier when GPU subdivision can be performed.
*/
char subsurf_apply_render;
char subsurf_use_optimal_display;
char _pad[2];
int subsurf_resolution;
void *_pad2;
/**
* Used to mark when derived data needs to be recalculated for a certain layer.
* Currently only normals.
* Caches for lazily computed vertex and polygon normals. These are stored here rather than in
* #CustomData because they can be calculated on a const mesh, and adding custom data layers on a
* const mesh is not thread-safe.
*/
char vert_normals_dirty;
char poly_normals_dirty;
float (*vert_normals)[3];
float (*poly_normals)[3];
int64_t cd_dirty_vert;
int64_t cd_dirty_edge;
int64_t cd_dirty_loop;
int64_t cd_dirty_poly;
void *_pad2;
} Mesh_Runtime;
typedef struct Mesh {

View File

@ -997,7 +997,7 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char *
/**
* Same as RNA_property_editable(), except this checks individual items in an array.
*/
bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index);
bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int index);
/**
* Without lib check, only checks the flag.

View File

@ -1898,59 +1898,63 @@ int RNA_property_ui_icon(const PropertyRNA *prop)
return rna_ensure_property((PropertyRNA *)prop)->icon;
}
bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop_orig)
static bool rna_property_editable_do(PointerRNA *ptr,
PropertyRNA *prop_orig,
const int index,
const char **r_info)
{
ID *id = ptr->owner_id;
int flag;
const char *dummy_info;
PropertyRNA *prop = rna_ensure_property(prop_orig);
flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag;
return (
(flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 &&
(!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) &&
(!ID_IS_OVERRIDE_LIBRARY(id) || RNA_property_overridable_get(ptr, prop_orig)))));
const char *info = "";
const int flag = (prop->itemeditable != NULL && index >= 0) ?
prop->itemeditable(ptr, index) :
(prop->editable != NULL ? prop->editable(ptr, &info) : prop->flag);
if (r_info != NULL) {
*r_info = info;
}
/* Early return if the property itself is not editable. */
if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER) != 0) {
if (r_info != NULL && (*r_info)[0] == '\0') {
*r_info = N_("This property is for internal use only and can't be edited");
}
return false;
}
/* If there is no owning ID, the property is editable at this point. */
if (id == NULL) {
return true;
}
/* Handle linked or liboverride ID cases. */
const bool is_linked_prop_exception = (prop->flag & PROP_LIB_EXCEPTION) != 0;
if (ID_IS_LINKED(id) && !is_linked_prop_exception) {
if (r_info != NULL && (*r_info)[0] == '\0') {
*r_info = N_("Can't edit this property from a linked data-block");
}
return false;
}
if (ID_IS_OVERRIDE_LIBRARY(id) && !RNA_property_overridable_get(ptr, prop_orig)) {
if (r_info != NULL && (*r_info)[0] == '\0') {
*r_info = N_("Can't edit this property from an override data-block");
}
return false;
}
/* At this point, property is owned by a local ID and therefore fully editable. */
return true;
}
bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop)
{
return rna_property_editable_do(ptr, prop, -1, NULL);
}
bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info)
{
ID *id = ptr->owner_id;
int flag;
PropertyRNA *prop_type = rna_ensure_property(prop);
*r_info = "";
/* get flag */
if (prop_type->editable) {
flag = prop_type->editable(ptr, r_info);
}
else {
flag = prop_type->flag;
if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER)) {
*r_info = N_("This property is for internal use only and can't be edited");
}
}
/* property from linked data-block */
if (id) {
if (ID_IS_LINKED(id) && (prop_type->flag & PROP_LIB_EXCEPTION) == 0) {
if (!(*r_info)[0]) {
*r_info = N_("Can't edit this property from a linked data-block");
}
return false;
}
if (ID_IS_OVERRIDE_LIBRARY(id)) {
if (!RNA_property_overridable_get(ptr, prop)) {
if (!(*r_info)[0]) {
*r_info = N_("Can't edit this property from an override data-block");
}
return false;
}
}
}
return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0);
return rna_property_editable_do(ptr, prop, -1, r_info);
}
bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop)
@ -1963,29 +1967,11 @@ bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop)
return (flag & PROP_EDITABLE) != 0;
}
bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index)
bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int index)
{
ID *id;
int flag;
BLI_assert(index >= 0);
prop = rna_ensure_property(prop);
flag = prop->flag;
if (prop->editable) {
const char *dummy_info;
flag &= prop->editable(ptr, &dummy_info);
}
if (prop->itemeditable) {
flag &= prop->itemeditable(ptr, index);
}
id = ptr->owner_id;
return (flag & PROP_EDITABLE) && (!id || !ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION));
return rna_property_editable_do(ptr, prop, index, NULL);
}
bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop)
@ -4918,7 +4904,7 @@ static char *rna_path_token_in_brackets(const char **path,
}
/**
* \return true when when the key in the path is correctly parsed and found in the collection
* \return true when the key in the path is correctly parsed and found in the collection
* or when the path is empty.
*/
static bool rna_path_parse_collection_key(const char **path,

View File

@ -649,7 +649,7 @@ const char *rna_translate_ui_text(const char *text,
struct PropertyRNA *prop,
bool translate);
/* Internal functions that cycles uses so we need to declare (tsk tsk) */
/* Internal functions that cycles uses so we need to declare (tsk!). */
void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values);
#ifdef RNA_RUNTIME

View File

@ -285,9 +285,6 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
mesh->runtime.is_original = false;
/* Mark tessellated CD layers as dirty. */
mesh->runtime.cd_dirty_vert |= CD_MASK_TESSLOOPNORMAL;
return mesh;
}

View File

@ -110,7 +110,7 @@ class SocketDeclaration {
/**
* Change the node such that the socket will become visible. The node type's update method
* should be called afterwards.
* \note Note that this is not necessarily implemented for all node types.
* \note this is not necessarily implemented for all node types.
*/
void make_available(bNode &node) const;

View File

@ -16,7 +16,7 @@ struct bContext;
namespace blender::nodes {
/**
* Parameters for the operation operation of adding a node after the link drag search menu closes.
* Parameters for the operation of adding a node after the link drag search menu closes.
*/
class LinkSearchOpParams {
private:

View File

@ -33,24 +33,24 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
{
switch (type) {
case GEO_NODE_CURVE_HANDLE_AUTO:
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
case GEO_NODE_CURVE_HANDLE_ALIGN:
return BezierSpline::HandleType::Align;
return BEZIER_HANDLE_ALIGN;
case GEO_NODE_CURVE_HANDLE_FREE:
return BezierSpline::HandleType::Free;
return BEZIER_HANDLE_FREE;
case GEO_NODE_CURVE_HANDLE_VECTOR:
return BezierSpline::HandleType::Vector;
return BEZIER_HANDLE_VECTOR;
}
BLI_assert_unreachable();
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
}
static void select_curve_by_handle_type(const CurveEval &curve,
const BezierSpline::HandleType type,
const HandleType type,
const GeometryNodeCurveHandleMode mode,
const MutableSpan<bool> r_selection)
{
@ -59,10 +59,10 @@ static void select_curve_by_handle_type(const CurveEval &curve,
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i_spline : range) {
const Spline &spline = *splines[i_spline];
if (spline.type() == Spline::Type::Bezier) {
if (spline.type() == CURVE_TYPE_BEZIER) {
const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
Span<BezierSpline::HandleType> types_left = bezier_spline.handle_types_left();
Span<BezierSpline::HandleType> types_right = bezier_spline.handle_types_right();
Span<int8_t> types_left = bezier_spline.handle_types_left();
Span<int8_t> types_right = bezier_spline.handle_types_right();
for (const int i_point : IndexRange(bezier_spline.size())) {
r_selection[offsets[i_spline] + i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT &&
types_left[i_point] == type) ||
@ -81,7 +81,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSelectHandles *storage =
(const NodeGeometryCurveSelectHandles *)params.node().storage;
const BezierSpline::HandleType handle_type = handle_type_from_input_type(
const HandleType handle_type = handle_type_from_input_type(
(GeometryNodeCurveHandleType)storage->handle_type);
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode;

View File

@ -31,20 +31,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
{
switch (type) {
case GEO_NODE_CURVE_HANDLE_AUTO:
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
case GEO_NODE_CURVE_HANDLE_ALIGN:
return BezierSpline::HandleType::Align;
return BEZIER_HANDLE_ALIGN;
case GEO_NODE_CURVE_HANDLE_FREE:
return BezierSpline::HandleType::Free;
return BEZIER_HANDLE_FREE;
case GEO_NODE_CURVE_HANDLE_VECTOR:
return BezierSpline::HandleType::Vector;
return BEZIER_HANDLE_VECTOR;
}
BLI_assert_unreachable();
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
}
static void node_geo_exec(GeoNodeExecParams params)
@ -70,17 +70,17 @@ static void node_geo_exec(GeoNodeExecParams params)
VArray<bool> selection = curve_component.attribute_get_for_read(
selection_name, ATTR_DOMAIN_POINT, true);
const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
const HandleType new_handle_type = handle_type_from_input_type(type);
int point_index = 0;
bool has_bezier_spline = false;
for (SplinePtr &spline : splines) {
if (spline->type() != Spline::Type::Bezier) {
if (spline->type() != CURVE_TYPE_BEZIER) {
point_index += spline->positions().size();
continue;
}
BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) {
/* In this case the automatically calculated handle types need to be "baked", because
* they're possibly changing from a type that is calculated automatically to a type that
* is positioned manually. */

View File

@ -148,8 +148,8 @@ static SplinePtr poly_to_bezier(const Spline &input)
output->positions().copy_from(input.positions());
output->radii().copy_from(input.radii());
output->tilts().copy_from(input.tilts());
output->handle_types_left().fill(BezierSpline::HandleType::Vector);
output->handle_types_right().fill(BezierSpline::HandleType::Vector);
output->handle_types_left().fill(BEZIER_HANDLE_VECTOR);
output->handle_types_right().fill(BEZIER_HANDLE_VECTOR);
output->set_resolution(12);
Spline::copy_base_settings(input, *output);
output->attributes = input.attributes;
@ -166,8 +166,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input)
scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right());
scale_input_assign<float>(input.radii(), 3, 2, output->radii());
scale_input_assign<float>(input.tilts(), 3, 2, output->tilts());
output->handle_types_left().fill(BezierSpline::HandleType::Align);
output->handle_types_right().fill(BezierSpline::HandleType::Align);
output->handle_types_left().fill(BEZIER_HANDLE_ALIGN);
output->handle_types_right().fill(BEZIER_HANDLE_ALIGN);
output->set_resolution(nurbs_spline.resolution());
Spline::copy_base_settings(input, *output);
output->attributes.reallocate(output->size());
@ -183,11 +183,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input)
static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
{
switch (input.type()) {
case Spline::Type::Bezier:
case CURVE_TYPE_BEZIER:
return input.copy();
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
return poly_to_bezier(input);
case Spline::Type::NURBS:
case CURVE_TYPE_NURBS:
if (input.size() < 6) {
params.error_message_add(
NodeWarningType::Info,
@ -202,6 +202,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params
}
return nurbs_to_bezier(input);
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
return {};
}
}
BLI_assert_unreachable();
return {};
@ -210,12 +214,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params
static SplinePtr convert_to_nurbs(const Spline &input)
{
switch (input.type()) {
case Spline::Type::NURBS:
case CURVE_TYPE_NURBS:
return input.copy();
case Spline::Type::Bezier:
case CURVE_TYPE_BEZIER:
return bezier_to_nurbs(input);
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
return poly_to_nurbs(input);
case CURVE_TYPE_CATMULL_ROM:
BLI_assert_unreachable();
return {};
}
BLI_assert_unreachable();
return {};

View File

@ -111,8 +111,8 @@ static void subdivide_bezier_segment(const BezierSpline &src,
MutableSpan<float3> dst_positions,
MutableSpan<float3> dst_handles_left,
MutableSpan<float3> dst_handles_right,
MutableSpan<BezierSpline::HandleType> dst_type_left,
MutableSpan<BezierSpline::HandleType> dst_type_right)
MutableSpan<int8_t> dst_type_left,
MutableSpan<int8_t> dst_type_right)
{
const bool is_last_cyclic_segment = index == (src.size() - 1);
const int next_index = is_last_cyclic_segment ? 0 : index + 1;
@ -122,10 +122,10 @@ static void subdivide_bezier_segment(const BezierSpline &src,
if (src.segment_is_vector(index)) {
if (is_last_cyclic_segment) {
dst_type_left.first() = BezierSpline::HandleType::Vector;
dst_type_left.first() = BEZIER_HANDLE_VECTOR;
}
dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector);
dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector);
dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR);
dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR);
const float factor_delta = 1.0f / result_size;
for (const int cut : IndexRange(result_size)) {
@ -136,10 +136,10 @@ static void subdivide_bezier_segment(const BezierSpline &src,
}
else {
if (is_last_cyclic_segment) {
dst_type_left.first() = BezierSpline::HandleType::Free;
dst_type_left.first() = BEZIER_HANDLE_FREE;
}
dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free);
dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free);
dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE);
dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE);
const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size;
@ -187,8 +187,8 @@ static void subdivide_bezier_spline(const BezierSpline &src,
MutableSpan<float3> dst_positions = dst.positions();
MutableSpan<float3> dst_handles_left = dst.handle_positions_left();
MutableSpan<float3> dst_handles_right = dst.handle_positions_right();
MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left();
MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right();
MutableSpan<int8_t> dst_type_left = dst.handle_types_left();
MutableSpan<int8_t> dst_type_right = dst.handle_types_right();
threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) {
for (const int i : range) {
@ -235,26 +235,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline,
subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii());
subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts());
switch (src_spline.type()) {
case Spline::Type::Poly: {
case CURVE_TYPE_POLY: {
const PolySpline &src = static_cast<const PolySpline &>(src_spline);
PolySpline &dst = static_cast<PolySpline &>(dst_spline);
subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
break;
}
case Spline::Type::Bezier: {
case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(src_spline);
BezierSpline &dst = static_cast<BezierSpline &>(dst_spline);
subdivide_bezier_spline(src, offsets, dst);
dst.mark_cache_invalid();
break;
}
case Spline::Type::NURBS: {
case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(src_spline);
NURBSpline &dst = static_cast<NURBSpline &>(dst_spline);
subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights());
break;
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
break;
}
}
}

View File

@ -111,9 +111,9 @@ static void spline_copy_builtin_attributes(const Spline &spline,
copy_data(spline.radii(), r_spline.radii(), mask);
copy_data(spline.tilts(), r_spline.tilts(), mask);
switch (spline.type()) {
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
break;
case Spline::Type::Bezier: {
case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(spline);
BezierSpline &dst = static_cast<BezierSpline &>(r_spline);
copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask);
@ -122,12 +122,16 @@ static void spline_copy_builtin_attributes(const Spline &spline,
copy_data(src.handle_types_right(), dst.handle_types_right(), mask);
break;
}
case Spline::Type::NURBS: {
case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(spline);
NURBSpline &dst = static_cast<NURBSpline &>(r_spline);
copy_data(src.weights(), dst.weights(), mask);
break;
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
break;
}
}
}

View File

@ -394,9 +394,9 @@ static void update_bezier_positions(const FilletData &fd,
dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] -
handle_length * next_dir;
dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] =
BezierSpline::HandleType::Align;
BEZIER_HANDLE_ALIGN;
dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] =
BezierSpline::HandleType::Vector;
BEZIER_HANDLE_VECTOR;
dst_spline.mark_cache_invalid();
/* Calculate the center of the radius to be formed. */
@ -406,8 +406,8 @@ static void update_bezier_positions(const FilletData &fd,
float radius;
radius_vec = math::normalize_and_get_length(radius_vec, radius);
dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align);
dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align);
dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
/* For each of the vertices in between the end points. */
for (const int j : IndexRange(1, count - 2)) {
@ -512,12 +512,12 @@ static SplinePtr fillet_spline(const Spline &spline,
copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src);
switch (spline.type()) {
case Spline::Type::Bezier: {
case CURVE_TYPE_BEZIER: {
const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline);
BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr);
if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
dst_spline.handle_types_left().fill(BezierSpline::HandleType::Vector);
dst_spline.handle_types_right().fill(BezierSpline::HandleType::Vector);
dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR);
dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR);
update_poly_positions(fd, dst_spline, src_spline, point_counts);
}
else {
@ -525,17 +525,21 @@ static SplinePtr fillet_spline(const Spline &spline,
}
break;
}
case Spline::Type::Poly: {
case CURVE_TYPE_POLY: {
update_poly_positions(fd, *dst_spline_ptr, spline, point_counts);
break;
}
case Spline::Type::NURBS: {
case CURVE_TYPE_NURBS: {
const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline);
NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr);
copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src);
update_poly_positions(fd, dst_spline, src_spline, point_counts);
break;
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
break;
}
}
return dst_spline_ptr;

View File

@ -31,30 +31,30 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
{
switch (type) {
case GEO_NODE_CURVE_HANDLE_AUTO:
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
case GEO_NODE_CURVE_HANDLE_ALIGN:
return BezierSpline::HandleType::Align;
return BEZIER_HANDLE_ALIGN;
case GEO_NODE_CURVE_HANDLE_FREE:
return BezierSpline::HandleType::Free;
return BEZIER_HANDLE_FREE;
case GEO_NODE_CURVE_HANDLE_VECTOR:
return BezierSpline::HandleType::Vector;
return BEZIER_HANDLE_VECTOR;
}
BLI_assert_unreachable();
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
}
static void select_by_handle_type(const CurveEval &curve,
const BezierSpline::HandleType type,
const HandleType type,
const GeometryNodeCurveHandleMode mode,
const MutableSpan<bool> r_selection)
{
int offset = 0;
for (const SplinePtr &spline : curve.splines()) {
if (spline->type() != Spline::Type::Bezier) {
if (spline->type() != CURVE_TYPE_BEZIER) {
r_selection.slice(offset, spline->size()).fill(false);
offset += spline->size();
}
@ -71,11 +71,11 @@ static void select_by_handle_type(const CurveEval &curve,
}
class HandleTypeFieldInput final : public GeometryFieldInput {
BezierSpline::HandleType type_;
HandleType type_;
GeometryNodeCurveHandleMode mode_;
public:
HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode)
HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode)
: GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
type_(type),
mode_(mode)
@ -124,7 +124,7 @@ class HandleTypeFieldInput final : public GeometryFieldInput {
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSelectHandles &storage = node_storage(params.node());
const BezierSpline::HandleType handle_type = handle_type_from_input_type(
const HandleType handle_type = handle_type_from_input_type(
(GeometryNodeCurveHandleType)storage.handle_type);
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode;

View File

@ -69,8 +69,8 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve(
spline->resize(2);
MutableSpan<float3> positions = spline->positions();
spline->handle_types_left().fill(BezierSpline::HandleType::Align);
spline->handle_types_right().fill(BezierSpline::HandleType::Align);
spline->handle_types_left().fill(BEZIER_HANDLE_ALIGN);
spline->handle_types_right().fill(BEZIER_HANDLE_ALIGN);
spline->radii().fill(1.0f);
spline->tilts().fill(0.0f);

View File

@ -33,20 +33,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
{
switch (type) {
case GEO_NODE_CURVE_HANDLE_AUTO:
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
case GEO_NODE_CURVE_HANDLE_ALIGN:
return BezierSpline::HandleType::Align;
return BEZIER_HANDLE_ALIGN;
case GEO_NODE_CURVE_HANDLE_FREE:
return BezierSpline::HandleType::Free;
return BEZIER_HANDLE_FREE;
case GEO_NODE_CURVE_HANDLE_VECTOR:
return BezierSpline::HandleType::Vector;
return BEZIER_HANDLE_VECTOR;
}
BLI_assert_unreachable();
return BezierSpline::HandleType::Auto;
return BEZIER_HANDLE_AUTO;
}
static void node_geo_exec(GeoNodeExecParams params)
@ -77,18 +77,18 @@ static void node_geo_exec(GeoNodeExecParams params)
selection_evaluator.evaluate();
const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
const HandleType new_handle_type = handle_type_from_input_type(type);
int point_index = 0;
for (SplinePtr &spline : splines) {
if (spline->type() != Spline::Type::Bezier) {
if (spline->type() != CURVE_TYPE_BEZIER) {
point_index += spline->positions().size();
continue;
}
has_bezier_spline = true;
BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) {
/* In this case the automatically calculated handle types need to be "baked", because
* they're possibly changing from a type that is calculated automatically to a type that
* is positioned manually. */

View File

@ -100,18 +100,22 @@ static Array<float> curve_length_point_domain(const CurveEval &curve)
MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())};
spline_factors.first() = 0.0f;
switch (splines[i]->type()) {
case Spline::Type::Bezier: {
case CURVE_TYPE_BEZIER: {
calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors);
break;
}
case Spline::Type::Poly: {
case CURVE_TYPE_POLY: {
calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors);
break;
}
case Spline::Type::NURBS: {
case CURVE_TYPE_NURBS: {
calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors);
break;
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
break;
}
}
}
});

View File

@ -259,8 +259,8 @@ static SplinePtr poly_to_bezier(const Spline &input)
output->positions().copy_from(input.positions());
output->radii().copy_from(input.radii());
output->tilts().copy_from(input.tilts());
output->handle_types_left().fill(BezierSpline::HandleType::Vector);
output->handle_types_right().fill(BezierSpline::HandleType::Vector);
output->handle_types_left().fill(BEZIER_HANDLE_VECTOR);
output->handle_types_right().fill(BEZIER_HANDLE_VECTOR);
output->set_resolution(12);
Spline::copy_base_settings(input, *output);
output->attributes = input.attributes;
@ -298,8 +298,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input)
nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode);
scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left());
scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right());
output->handle_types_left().fill(BezierSpline::HandleType::Align);
output->handle_types_right().fill(BezierSpline::HandleType::Align);
output->handle_types_left().fill(BEZIER_HANDLE_ALIGN);
output->handle_types_right().fill(BEZIER_HANDLE_ALIGN);
output->set_resolution(nurbs_spline.resolution());
Spline::copy_base_settings(nurbs_spline, *output);
output->attributes.reallocate(output->size());
@ -315,11 +315,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input)
static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
{
switch (input.type()) {
case Spline::Type::Bezier:
case CURVE_TYPE_BEZIER:
return input.copy();
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
return poly_to_bezier(input);
case Spline::Type::NURBS:
case CURVE_TYPE_NURBS:
if (input.size() < 4) {
params.error_message_add(
NodeWarningType::Info,
@ -327,6 +327,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params
return input.copy();
}
return nurbs_to_bezier(input);
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
return {};
}
}
BLI_assert_unreachable();
return {};
@ -335,12 +339,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params
static SplinePtr convert_to_nurbs(const Spline &input)
{
switch (input.type()) {
case Spline::Type::NURBS:
case CURVE_TYPE_NURBS:
return input.copy();
case Spline::Type::Bezier:
case CURVE_TYPE_BEZIER:
return bezier_to_nurbs(input);
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
return poly_to_nurbs(input);
case CURVE_TYPE_CATMULL_ROM:
BLI_assert_unreachable();
return {};
}
BLI_assert_unreachable();
return {};

View File

@ -93,8 +93,8 @@ static void subdivide_bezier_segment(const BezierSpline &src,
MutableSpan<float3> dst_positions,
MutableSpan<float3> dst_handles_left,
MutableSpan<float3> dst_handles_right,
MutableSpan<BezierSpline::HandleType> dst_type_left,
MutableSpan<BezierSpline::HandleType> dst_type_right)
MutableSpan<int8_t> dst_type_left,
MutableSpan<int8_t> dst_type_right)
{
const bool is_last_cyclic_segment = index == (src.size() - 1);
const int next_index = is_last_cyclic_segment ? 0 : index + 1;
@ -104,10 +104,10 @@ static void subdivide_bezier_segment(const BezierSpline &src,
if (src.segment_is_vector(index)) {
if (is_last_cyclic_segment) {
dst_type_left.first() = BezierSpline::HandleType::Vector;
dst_type_left.first() = BEZIER_HANDLE_VECTOR;
}
dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector);
dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector);
dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR);
dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR);
const float factor_delta = 1.0f / result_size;
for (const int cut : IndexRange(result_size)) {
@ -118,10 +118,10 @@ static void subdivide_bezier_segment(const BezierSpline &src,
}
else {
if (is_last_cyclic_segment) {
dst_type_left.first() = BezierSpline::HandleType::Free;
dst_type_left.first() = BEZIER_HANDLE_FREE;
}
dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free);
dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free);
dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE);
dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE);
const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size;
@ -169,8 +169,8 @@ static void subdivide_bezier_spline(const BezierSpline &src,
MutableSpan<float3> dst_positions = dst.positions();
MutableSpan<float3> dst_handles_left = dst.handle_positions_left();
MutableSpan<float3> dst_handles_right = dst.handle_positions_right();
MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left();
MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right();
MutableSpan<int8_t> dst_type_left = dst.handle_types_left();
MutableSpan<int8_t> dst_type_right = dst.handle_types_right();
threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) {
for (const int i : range) {
@ -217,26 +217,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline,
subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii());
subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts());
switch (src_spline.type()) {
case Spline::Type::Poly: {
case CURVE_TYPE_POLY: {
const PolySpline &src = static_cast<const PolySpline &>(src_spline);
PolySpline &dst = static_cast<PolySpline &>(dst_spline);
subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
break;
}
case Spline::Type::Bezier: {
case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(src_spline);
BezierSpline &dst = static_cast<BezierSpline &>(dst_spline);
subdivide_bezier_spline(src, offsets, dst);
dst.mark_cache_invalid();
break;
}
case Spline::Type::NURBS: {
case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(src_spline);
NURBSpline &dst = static_cast<NURBSpline &>(dst_spline);
subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights());
break;
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
break;
}
}
}

View File

@ -367,15 +367,18 @@ static void trim_spline(SplinePtr &spline,
const Spline::LookupResult end)
{
switch (spline->type()) {
case Spline::Type::Bezier:
case CURVE_TYPE_BEZIER:
trim_bezier_spline(*spline, start, end);
break;
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
trim_poly_spline(*spline, start, end);
break;
case Spline::Type::NURBS:
case CURVE_TYPE_NURBS:
spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end));
break;
case CURVE_TYPE_CATMULL_ROM:
BLI_assert_unreachable();
spline = {};
}
spline->mark_cache_invalid();
}
@ -400,8 +403,8 @@ static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &l
const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion(
trim.left_index, trim.right_index, trim.factor);
bezier.positions().first() = new_point.position;
bezier.handle_types_left().first() = BezierSpline::HandleType::Free;
bezier.handle_types_right().first() = BezierSpline::HandleType::Free;
bezier.handle_types_left().first() = BEZIER_HANDLE_FREE;
bezier.handle_types_right().first() = BEZIER_HANDLE_FREE;
bezier.handle_positions_left().first() = new_point.left_handle;
bezier.handle_positions_right().first() = new_point.right_handle;
@ -477,15 +480,18 @@ static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::Look
static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup)
{
switch (spline->type()) {
case Spline::Type::Bezier:
case CURVE_TYPE_BEZIER:
to_single_point_bezier(*spline, lookup);
break;
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
to_single_point_poly(*spline, lookup);
break;
case Spline::Type::NURBS:
case CURVE_TYPE_NURBS:
spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup));
break;
case CURVE_TYPE_CATMULL_ROM:
BLI_assert_unreachable();
spline = {};
}
}

View File

@ -332,9 +332,9 @@ static void spline_copy_builtin_attributes(const Spline &spline,
copy_data_based_on_mask(spline.radii(), r_spline.radii(), mask);
copy_data_based_on_mask(spline.tilts(), r_spline.tilts(), mask);
switch (spline.type()) {
case Spline::Type::Poly:
case CURVE_TYPE_POLY:
break;
case Spline::Type::Bezier: {
case CURVE_TYPE_BEZIER: {
const BezierSpline &src = static_cast<const BezierSpline &>(spline);
BezierSpline &dst = static_cast<BezierSpline &>(r_spline);
copy_data_based_on_mask(src.handle_positions_left(), dst.handle_positions_left(), mask);
@ -343,12 +343,16 @@ static void spline_copy_builtin_attributes(const Spline &spline,
copy_data_based_on_mask(src.handle_types_right(), dst.handle_types_right(), mask);
break;
}
case Spline::Type::NURBS: {
case CURVE_TYPE_NURBS: {
const NURBSpline &src = static_cast<const NURBSpline &>(spline);
NURBSpline &dst = static_cast<NURBSpline &>(r_spline);
copy_data_based_on_mask(src.weights(), dst.weights(), mask);
break;
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
break;
}
}
}

View File

@ -302,7 +302,6 @@ static void extrude_mesh_vertices(MeshComponent &component,
}
BKE_mesh_runtime_clear_cache(&mesh);
BKE_mesh_normals_tag_dirty(&mesh);
}
static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh)
@ -626,7 +625,6 @@ static void extrude_mesh_edges(MeshComponent &component,
}
BKE_mesh_runtime_clear_cache(&mesh);
BKE_mesh_normals_tag_dirty(&mesh);
}
/**
@ -995,7 +993,6 @@ static void extrude_mesh_face_regions(MeshComponent &component,
}
BKE_mesh_runtime_clear_cache(&mesh);
BKE_mesh_normals_tag_dirty(&mesh);
}
/* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */
@ -1263,7 +1260,6 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
}
BKE_mesh_runtime_clear_cache(&mesh);
BKE_mesh_normals_tag_dirty(&mesh);
}
static void node_geo_exec(GeoNodeExecParams params)

View File

@ -52,18 +52,22 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve)
const Spline &spline = *splines[i];
MutableSpan spline_tangents{tangents.as_mutable_span().slice(offsets[i], spline.size())};
switch (splines[i]->type()) {
case Spline::Type::Bezier: {
case CURVE_TYPE_BEZIER: {
calculate_bezier_tangents(static_cast<const BezierSpline &>(spline), spline_tangents);
break;
}
case Spline::Type::Poly: {
case CURVE_TYPE_POLY: {
calculate_poly_tangents(static_cast<const PolySpline &>(spline), spline_tangents);
break;
}
case Spline::Type::NURBS: {
case CURVE_TYPE_NURBS: {
calculate_nurbs_tangents(static_cast<const NURBSpline &>(spline), spline_tangents);
break;
}
case CURVE_TYPE_CATMULL_ROM: {
BLI_assert_unreachable();
break;
}
}
}
});
@ -83,7 +87,7 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp
/* Use a reference to evaluated tangents if possible to avoid an allocation and a copy.
* This is only possible when there is only one poly spline. */
if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
if (splines.size() == 1 && splines.first()->type() == CURVE_TYPE_POLY) {
const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
return VArray<float3>::ForSpan(spline.evaluated_tangents());
}

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