* Merge branch 'master' into temp_bmesh_multires

* Implemented DynTopo option to dissolve 3 and 4-valence
  vertices.
* Fixed bug in dyntopo collapse customdata snapping.
This commit is contained in:
Joseph Eagar 2021-04-11 17:58:51 -07:00
commit 64337d087d
335 changed files with 5185 additions and 2976 deletions

View File

@ -35,7 +35,6 @@ Checks: >
-modernize-use-auto,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
-modernize-use-equals-default,
-modernize-use-nodiscard,
-modernize-loop-convert,
-modernize-pass-by-value,

View File

@ -75,7 +75,7 @@ FIND_PATH(OSL_SHADER_DIR
/usr/share/OSL/
/usr/include/OSL/
PATH_SUFFIXES
shaders
share/OSL/shaders
)
# handle the QUIETLY and REQUIRED arguments and set OSL_FOUND to TRUE if

View File

@ -22,7 +22,7 @@
This script generates the blender.1 man page, embedding the help text
from the Blender executable itself. Invoke it as follows:
blender.1.py <path-to-blender> <output-filename>
blender.1.py --blender <path-to-blender> --output <output-filename>
where <path-to-blender> is the path to the Blender executable,
and <output-filename> is where to write the generated man page.
@ -30,108 +30,147 @@ and <output-filename> is where to write the generated man page.
# <pep8 compliant>
import argparse
import os
import subprocess
import sys
import time
from typing import (
Dict,
TextIO,
)
def man_format(data):
def man_format(data: str) -> str:
data = data.replace("-", "\\-")
data = data.replace("\t", " ")
return data
if len(sys.argv) != 3:
import getopt
raise getopt.GetoptError("Usage: %s <path-to-blender> <output-filename>" % sys.argv[0])
def blender_extract_info(blender_bin: str) -> Dict[str, str]:
blender_bin = sys.argv[1]
outfilename = sys.argv[2]
blender_env = {
"ASAN_OPTIONS": "exitcode=0:" + os.environ.get("ASAN_OPTIONS", ""),
}
cmd = [blender_bin, "--help"]
print(" executing:", " ".join(cmd))
ASAN_OPTIONS = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "")
blender_help = subprocess.run(
cmd, env={"ASAN_OPTIONS": ASAN_OPTIONS}, check=True, stdout=subprocess.PIPE).stdout.decode(encoding="utf-8")
blender_version = subprocess.run(
[blender_bin, "--version"], env={"ASAN_OPTIONS": ASAN_OPTIONS}, check=True, stdout=subprocess.PIPE).stdout.decode(encoding="utf-8").strip()
blender_version, blender_date = (blender_version.split("build") + [None, None])[0:2]
blender_version = blender_version.rstrip().partition(" ")[2] # remove 'Blender' prefix.
if blender_date is None:
# Happens when built without WITH_BUILD_INFO e.g.
date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
else:
blender_date = blender_date.strip().partition(" ")[2] # remove 'date:' prefix
date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
blender_help = subprocess.run(
[blender_bin, "--help"],
env=blender_env,
check=True,
stdout=subprocess.PIPE,
).stdout.decode(encoding="utf-8")
outfile = open(outfilename, "w")
fw = outfile.write
blender_version_ouput = subprocess.run(
[blender_bin, "--version"],
env=blender_env,
check=True,
stdout=subprocess.PIPE,
).stdout.decode(encoding="utf-8")
fw('.TH "BLENDER" "1" "%s" "Blender %s"\n' % (date_string, blender_version.replace(".", "\\&.")))
# Extract information from the version string.
# Note that some internal modules may print errors (e.g. color management),
# check for each lines prefix to ensure these aren't included.
blender_version = ""
blender_date = ""
for l in blender_version_ouput.split("\n"):
if l.startswith("Blender "):
# Remove 'Blender' prefix.
blender_version = l.split(" ", 1)[1].strip()
elif l.lstrip().startswith("build date:"):
# Remove 'build date:' prefix.
blender_date = l.split(":", 1)[1].strip()
if blender_version and blender_date:
break
fw('''
if not blender_date:
# Happens when built without WITH_BUILD_INFO e.g.
date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
else:
date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
return {
"help": blender_help,
"version": blender_version,
"date": date_string,
}
def man_page_from_blender_help(fh: TextIO, blender_bin: str) -> None:
blender_info = blender_extract_info(blender_bin)
# Header Content.
fh.write(
'.TH "BLENDER" "1" "%s" "Blender %s"\n' %
(blender_info["date"], blender_info["version"].replace(".", "\\&."))
)
fh.write(r'''
.SH NAME
blender \- a full-featured 3D application''')
fw('''
fh.write(r'''
.SH SYNOPSIS
.B blender [args ...] [file] [args ...]''')
fw('''
fh.write(r'''
.br
.SH DESCRIPTION
.PP
.B blender
is a full-featured 3D application. It supports the entirety of the 3D pipeline - modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
is a full-featured 3D application. It supports the entirety of the 3D pipeline - '''
'''modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
Use Blender to create 3D images and animations, films and commercials, content for games, architectural and industrial visualizatons, and scientific visualizations.
Use Blender to create 3D images and animations, films and commercials, content for games, '''
r'''architectural and industrial visualizatons, and scientific visualizations.
https://www.blender.org''')
fw('''
fh.write(r'''
.SH OPTIONS''')
fw("\n\n")
fh.write("\n\n")
lines = [line.rstrip() for line in blender_help.split("\n")]
# Body Content.
while lines:
l = lines.pop(0)
if l.startswith("Environment Variables:"):
fw('.SH "ENVIRONMENT VARIABLES"\n')
elif l.endswith(":"): # one line
fw('.SS "%s"\n\n' % l)
elif l.startswith("-") or l.startswith("/"): # can be multi line
lines = [line.rstrip() for line in blender_info["help"].split("\n")]
fw('.TP\n')
fw('.B %s\n' % man_format(l))
while lines:
l = lines.pop(0)
if l.startswith("Environment Variables:"):
fh.write('.SH "ENVIRONMENT VARIABLES"\n')
elif l.endswith(":"): # One line.
fh.write('.SS "%s"\n\n' % l)
elif l.startswith("-") or l.startswith("/"): # Can be multi line.
fh.write('.TP\n')
fh.write('.B %s\n' % man_format(l))
while lines:
# line with no
if lines[0].strip() and len(lines[0].lstrip()) == len(lines[0]): # no white space
break
while lines:
# line with no
if lines[0].strip() and len(lines[0].lstrip()) == len(lines[0]): # No white space.
break
if not l: # second blank line
fw('.IP\n')
else:
fw('.br\n')
if not l: # Second blank line.
fh.write('.IP\n')
else:
fh.write('.br\n')
l = lines.pop(0)
l = l[1:] # remove first whitespace (tab)
l = lines.pop(0)
if l:
assert(l.startswith('\t'))
l = l[1:] # Remove first white-space (tab).
fw('%s\n' % man_format(l))
fh.write('%s\n' % man_format(l))
else:
if not l.strip():
fw('.br\n')
else:
fw('%s\n' % man_format(l))
if not l.strip():
fh.write('.br\n')
else:
fh.write('%s\n' % man_format(l))
# footer
# Footer Content.
fw('''
fh.write(r'''
.br
.SH SEE ALSO
.B luxrender(1)
@ -143,5 +182,33 @@ This manpage was written for a Debian GNU/Linux system by Daniel Mester
<cyril.brulebois@enst-bretagne.fr> and Dan Eicher <dan@trollwerks.org>.
''')
outfile.close()
print("written:", outfilename)
def create_argparse() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument(
"--output",
required=True,
help="The man page to write to."
)
parser.add_argument(
"--blender",
required=True,
help="Path to the blender binary."
)
return parser
def main() -> None:
parser = create_argparse()
args = parser.parse_args()
blender_bin = args.blender
output_filename = args.output
with open(output_filename, "w", encoding="utf-8") as fh:
man_page_from_blender_help(fh, blender_bin)
if __name__ == "__main__":
main()

View File

@ -219,9 +219,12 @@ def do_versions(self):
if version <= (2, 93, 16):
cscene = scene.cycles
if scene.render.use_simplify and \
(cscene.ao_bounces or cscene.ao_bounces_render):
ao_bounces = cscene.get("ao_bounces", 0)
ao_bounces_render = cscene.get("ao_bounces_render", 0)
if scene.render.use_simplify and (ao_bounces or ao_bounces_render):
cscene.use_fast_gi = True
cscene.ao_bounces = ao_bounces
cscene.ao_bounces_render = ao_bounces_render
else:
cscene.ao_bounces = 1
cscene.ao_bounces_render = 1

View File

@ -26,7 +26,7 @@
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const struct Volume *volume,
struct VolumeGrid *grid);
const struct VolumeGrid *grid);
#endif
CCL_NAMESPACE_BEGIN
@ -227,7 +227,7 @@ class BlenderVolumeLoader : public VDBImageLoader {
const bool unload = !b_volume_grid.is_loaded();
::Volume *volume = (::Volume *)b_volume.ptr.data;
VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
const VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
if (unload) {

View File

@ -299,7 +299,7 @@ class Scene : public NodeOwner {
* node array (e.g. Scene::geometry for Geometry nodes) and tag the appropriate
* manager for an update.
*/
template<typename T, typename... Args> T *create_node(Args &&...args)
template<typename T, typename... Args> T *create_node(Args &&... args)
{
T *node = new T(args...);
node->set_owner(this);

View File

@ -788,7 +788,7 @@ ccl_device_inline float compare_floats(float a, float b, float abs_diff, int ulp
}
/* Calculate the angle between the two vectors a and b.
* The usual approach acos(dot(a, b)) has severe precision issues for small angles,
* The usual approach `acos(dot(a, b))` has severe precision issues for small angles,
* which are avoided by this method.
* Based on "Mangled Angles" from https://people.eecs.berkeley.edu/~wkahan/Mindless.pdf
*/

View File

@ -1131,7 +1131,8 @@ GHOST_TSuccess GHOST_WindowCocoa::hasCursorShape(GHOST_TStandardCursor shape)
return success;
}
/** Reverse the bits in a GHOST_TUns8
/* Reverse the bits in a GHOST_TUns8 */
#if 0
static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
{
ch= ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
@ -1139,7 +1140,7 @@ static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
ch= ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
return ch;
}
*/
#endif
/** Reverse the bits in a GHOST_TUns16 */
static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)

View File

@ -555,7 +555,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
/* Wintab API */
struct {
/** WinTab DLL handle. */
/** `WinTab.dll` handle. */
HMODULE handle = NULL;
/** API functions */
@ -574,7 +574,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TWindowState m_normal_state;
/** user32 dll handle*/
/** `user32.dll` handle */
HMODULE m_user32;
GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory;
GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory;

View File

@ -57,9 +57,11 @@
extern "C" {
#endif
/** Returns the length of the allocated memory segment pointed at
/**
* Returns the length of the allocated memory segment pointed at
* by vmemh. If the pointer was not previously allocated by this
* module, the result is undefined.*/
* module, the result is undefined.
*/
extern size_t (*MEM_allocN_len)(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
/**
@ -103,7 +105,8 @@ extern void *(*MEM_recallocN_id)(void *vmemh,
/**
* Allocate a block of memory of size len, with tag name str. The
* memory is cleared. The name must be static, because only a
* pointer to it is stored ! */
* pointer to it is stored!
*/
extern void *(*MEM_callocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
@ -143,12 +146,15 @@ extern void *(*MEM_mallocN_aligned)(size_t len,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3);
/** Print a list of the names and sizes of all allocated memory
* blocks. as a python dict for easy investigation */
/**
* Print a list of the names and sizes of all allocated memory
* blocks. as a python dict for easy investigation.
*/
extern void (*MEM_printmemlist_pydict)(void);
/** Print a list of the names and sizes of all allocated memory
* blocks. */
/**
* Print a list of the names and sizes of all allocated memory blocks.
*/
extern void (*MEM_printmemlist)(void);
/** calls the function on all allocated memory blocks. */
@ -163,7 +169,8 @@ extern void (*MEM_set_error_callback)(void (*func)(const char *));
/**
* Are the start/end block markers still correct ?
*
* \retval true for correct memory, false for corrupted memory. */
* \retval true for correct memory, false for corrupted memory.
*/
extern bool (*MEM_consistency_check)(void);
/** Attempt to enforce OSX (or other OS's) to have malloc and stack nonzero */
@ -209,8 +216,10 @@ extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT;
extern const char *(*MEM_name_ptr)(void *vmemh);
#endif
/** This should be called as early as possible in the program. When it has been called, information
* about memory leaks will be printed on exit. */
/**
* This should be called as early as possible in the program. When it has been called, information
* about memory leaks will be printed on exit.
*/
void MEM_init_memleak_detection(void);
/**
@ -219,9 +228,11 @@ void MEM_init_memleak_detection(void);
*/
void MEM_use_memleak_detection(bool enabled);
/** When this has been called and memory leaks have been detected, the process will have an exit
/**
* When this has been called and memory leaks have been detected, the process will have an exit
* code that indicates failure. This can be used for when checking for memory leaks with automated
* tests. */
* tests.
*/
void MEM_enable_fail_on_memleak(void);
/* Switch allocator to fast mode, with less tracking.

View File

@ -35,7 +35,7 @@ Development
License
-------
Blender as a whole is licensed under the GNU Public License, Version 3.
Blender as a whole is licensed under the GNU General Public License, Version 3.
Individual files may have a different, but compatible license.
See `blender.org/about/license <https://www.blender.org/about/license>`__ for details.

View File

@ -20,7 +20,6 @@
from __future__ import annotations
import bpy
import nodeitems_utils
from bpy.types import (
Operator,
PropertyGroup,
@ -195,6 +194,8 @@ class NODE_OT_add_search(NodeAddOperator, Operator):
# Create an enum list from node items
def node_enum_items(self, context):
import nodeitems_utils
enum_items = NODE_OT_add_search._enum_item_hack
enum_items.clear()
@ -210,6 +211,8 @@ class NODE_OT_add_search(NodeAddOperator, Operator):
# Look up the item based on index
def find_node_item(self, context):
import nodeitems_utils
node_item = int(self.node_item)
for index, item in enumerate(nodeitems_utils.node_items_iter(context)):
if index == node_item:

View File

@ -38,13 +38,12 @@ class PhysicButtonsPanel:
def physics_add(layout, md, name, type, typeicon, toggles):
row = layout.row(align=True)
if md:
row.context_pointer_set("modifier", md)
row.operator(
"object.modifier_remove",
text=name,
text_ctxt=i18n_contexts.default,
icon='X',
)
).modifier = md.name
if toggles:
row.prop(md, "show_viewport", text="")
row.prop(md, "show_render", text="")

View File

@ -18,7 +18,6 @@
# <pep8 compliant>
import bpy
import nodeitems_utils
from bpy.types import Header, Menu, Panel
from bpy.app.translations import pgettext_iface as iface_
from bpy.app.translations import contexts as i18n_contexts
@ -225,6 +224,8 @@ class NODE_MT_add(bpy.types.Menu):
bl_translation_context = i18n_contexts.operator_default
def draw(self, context):
import nodeitems_utils
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'

View File

@ -51,13 +51,13 @@ class OUTLINER_HT_header(Header):
row.prop(space, "use_sync_select", icon='UV_SYNC_SELECT', text="")
row = layout.row(align=True)
if display_mode in {'SCENES', 'VIEW_LAYER'}:
if display_mode in {'SCENES', 'VIEW_LAYER', 'LIBRARY_OVERRIDES'}:
row.popover(
panel="OUTLINER_PT_filter",
text="",
icon='FILTER',
)
elif display_mode in {'LIBRARIES', 'ORPHAN_DATA'}:
if display_mode in {'LIBRARIES', 'LIBRARY_OVERRIDES', 'ORPHAN_DATA'}:
row.prop(space, "use_filter_id_type", text="", icon='FILTER')
sub = row.row(align=True)
sub.active = space.use_filter_id_type
@ -341,19 +341,26 @@ class OUTLINER_PT_filter(Panel):
col = layout.column(align=True)
col.prop(space, "use_sort_alpha")
row = layout.row(align=True)
row.prop(space, "use_sync_select", text="Sync Selection")
if display_mode not in {'LIBRARY_OVERRIDES'}:
row = layout.row(align=True)
row.prop(space, "use_sync_select", text="Sync Selection")
row = layout.row(align=True)
row.prop(space, "show_mode_column", text="Show Mode Column")
layout.separator()
row = layout.row(align=True)
row.prop(space, "show_mode_column", text="Show Mode Column")
layout.separator()
col = layout.column(align=True)
col.label(text="Search")
col.prop(space, "use_filter_complete", text="Exact Match")
col.prop(space, "use_filter_case_sensitive", text="Case Sensitive")
if display_mode != 'VIEW_LAYER':
if display_mode in {'LIBRARY_OVERRIDES'} and bpy.data.libraries:
col.separator()
row = col.row()
row.label(icon='LIBRARY_DATA_OVERRIDE')
row.prop(space, "use_filter_lib_override_system", text="System Overrides")
if display_mode not in {'VIEW_LAYER'}:
return
layout.separator()
@ -405,10 +412,6 @@ class OUTLINER_PT_filter(Panel):
row = sub.row()
row.label(icon='EMPTY_DATA')
row.prop(space, "use_filter_object_empty", text="Empties")
row = sub.row()
if bpy.data.libraries:
row.label(icon='LIBRARY_DATA_OVERRIDE')
row.prop(space, "use_filter_lib_override", text="Library Overrides")
if (
bpy.data.curves or
@ -425,6 +428,16 @@ class OUTLINER_PT_filter(Panel):
row.label(icon='BLANK1')
row.prop(space, "use_filter_object_others", text="Others")
if bpy.data.libraries:
col.separator()
row = col.row()
row.label(icon='LIBRARY_DATA_OVERRIDE')
row.prop(space, "use_filter_lib_override", text="Library Overrides")
row = col.row()
row.label(icon='LIBRARY_DATA_OVERRIDE')
row.prop(space, "use_filter_lib_override_system", text="System Overrides")
classes = (
OUTLINER_HT_header,

View File

@ -812,6 +812,7 @@ class VIEW3D_PT_sculpt_dyntopo_advanced(Panel, View3DPaintPanel):
col = layout.column()
do_prop("subdivide")
do_prop("collapse")
do_prop("cleanup")
do_prop("spacing")
do_prop("detail_range")
do_prop("detail_percent")
@ -870,6 +871,7 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}:
col.operator("sculpt.detail_flood_fill")
col.prop(sculpt, "use_dyntopo_cleanup")
col.prop(sculpt, "use_smooth_shading")
col.prop(sculpt, "use_flat_vcol_shading")

View File

@ -505,6 +505,7 @@ geometry_node_categories = [
NodeItem("ShaderNodeCombineRGB"),
]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
NodeItem("GeometryNodeBoundBox"),
NodeItem("GeometryNodeTransform"),
NodeItem("GeometryNodeJoinGeometry"),
]),

View File

@ -26,22 +26,28 @@
extern "C" {
#endif
struct ListBase;
struct CurveCache;
struct Object;
struct Path;
/* ---------------------------------------------------- */
/* Curve Paths */
void free_path(struct Path *path);
void calc_curvepath(struct Object *ob, struct ListBase *nurbs);
bool where_on_path(const struct Object *ob,
float ctime,
float r_vec[4],
float r_dir[3],
float r_quat[4],
float *r_radius,
float *r_weight);
int BKE_anim_path_get_array_size(const struct CurveCache *curve_cache);
float BKE_anim_path_get_length(const struct CurveCache *curve_cache);
/* This function populates the 'ob->runtime.curve_cache->anim_path_accum_length' data.
* You should never have to call this manually as it should already have been called by
* 'BKE_displist_make_curveTypes'. Do not call this manually unless you know what you are doing.
*/
void BKE_anim_path_calc_data(struct Object *ob);
bool BKE_where_on_path(const struct Object *ob,
float ctime,
float r_vec[4],
float r_dir[3],
float r_quat[4],
float *r_radius,
float *r_weight);
#ifdef __cplusplus
}

View File

@ -227,6 +227,8 @@ void BKE_scene_objects_iterator_begin(struct BLI_Iterator *iter, void *data_in);
void BKE_scene_objects_iterator_next(struct BLI_Iterator *iter);
void BKE_scene_objects_iterator_end(struct BLI_Iterator *iter);
struct GSet *BKE_scene_objects_as_gset(struct Scene *scene, struct GSet *objects_gset);
#define FOREACH_SCENE_COLLECTION_BEGIN(scene, _instance) \
ITER_BEGIN (BKE_scene_collections_iterator_begin, \
BKE_scene_collections_iterator_next, \

View File

@ -50,7 +50,13 @@ typedef struct CurveCache {
ListBase disp;
ListBase bev;
ListBase deformed_nurbs;
struct Path *path;
/* This array contains the accumulative length of the curve segments.
* So you can see this as a "total distance traveled" along the curve.
* The first entry is the length between point 0 and 1 while the last is the
* total length of the curve.
*
* Used by #BKE_where_on_path. */
const float *anim_path_accum_length;
} CurveCache;
/* Definitions needed for shape keys */

View File

@ -135,12 +135,18 @@ class GeometryComponent {
public:
GeometryComponent(GeometryComponentType type);
virtual ~GeometryComponent();
virtual ~GeometryComponent() = default;
static GeometryComponent *create(GeometryComponentType component_type);
/* The returned component should be of the same type as the type this is called on. */
virtual GeometryComponent *copy() const = 0;
/* Direct data is everything except for instances of objects/collections.
* If this returns true, the geometry set can be cached and is still valid after e.g. modifier
* evaluation ends. Instances can only be valid as long as the data they instance is valid. */
virtual bool owns_direct_data() const = 0;
virtual void ensure_owns_direct_data() = 0;
void user_add() const;
void user_remove() const;
bool is_mutable() const;
@ -180,7 +186,7 @@ class GeometryComponent {
const CustomDataType data_type);
blender::Set<std::string> attribute_names() const;
void attribute_foreach(const AttributeForeachCallback callback) const;
bool attribute_foreach(const AttributeForeachCallback callback) const;
virtual bool is_empty() const;
@ -315,6 +321,8 @@ struct GeometrySet {
void clear();
void ensure_owns_direct_data();
/* Utility methods for creation. */
static GeometrySet create_with_mesh(
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
@ -374,6 +382,9 @@ class MeshComponent : public GeometryComponent {
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_MESH;
private:
@ -404,6 +415,9 @@ class PointCloudComponent : public GeometryComponent {
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
private:
@ -444,6 +458,9 @@ class InstancesComponent : public GeometryComponent {
bool is_empty() const final;
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES;
};
@ -466,5 +483,8 @@ class VolumeComponent : public GeometryComponent {
const Volume *get_for_read() const;
Volume *get_for_write();
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME;
};

View File

@ -39,6 +39,10 @@ struct GeometryInstanceGroup {
Vector<float4x4> transforms;
};
void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
const AttributeForeachCallback callback,
const int limit);
void geometry_set_gather_instances(const GeometrySet &geometry_set,
Vector<GeometryInstanceGroup> &r_instance_groups);
@ -55,9 +59,9 @@ struct AttributeKind {
* will contain the highest complexity data type and the highest priority domain among every
* attribute with the given name on all of the input components.
*/
void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
Span<GeometryComponentType> component_types,
Span<bke::GeometryInstanceGroup> set_groups,
const Set<std::string> &ignored_attributes);
void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
Span<GeometryComponentType> component_types,
const Set<std::string> &ignored_attributes,
Map<std::string, AttributeKind> &r_attributes);
} // namespace blender::bke

View File

@ -116,6 +116,8 @@ enum {
LIB_ID_COPY_NO_ANIMDATA = 1 << 19,
/** Mesh: Reference CD data layers instead of doing real copy - USE WITH CAUTION! */
LIB_ID_COPY_CD_REFERENCE = 1 << 20,
/** Do not copy id->override_library, used by ID datablock override routines. */
LIB_ID_COPY_NO_LIB_OVERRIDE = 1 << 21,
/* *** XXX Hackish/not-so-nice specific behaviors needed for some corner cases. *** */
/* *** Ideally we should not have those, but we need them for now... *** */
@ -136,7 +138,8 @@ enum {
LIB_ID_CREATE_LOCALIZE = LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_DEG_TAG,
/** Generate a local copy, outside of bmain, to work on (used by COW e.g.). */
LIB_ID_COPY_LOCALIZE = LIB_ID_CREATE_LOCALIZE | LIB_ID_COPY_NO_PREVIEW | LIB_ID_COPY_CACHES,
LIB_ID_COPY_LOCALIZE = LIB_ID_CREATE_LOCALIZE | LIB_ID_COPY_NO_PREVIEW | LIB_ID_COPY_CACHES |
LIB_ID_COPY_NO_LIB_OVERRIDE,
};
void BKE_libblock_copy_ex(struct Main *bmain,

View File

@ -84,7 +84,8 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
struct ViewLayer *view_layer,
struct ID *id_root,
struct Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce);
const bool do_hierarchy_enforce,
const bool do_post_process);
void BKE_lib_override_library_main_resync(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer);

View File

@ -1398,6 +1398,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_MESH_PRIMITIVE_GRID 1039
#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
#define GEO_NODE_ATTRIBUTE_CLAMP 1041
#define GEO_NODE_BOUNDING_BOX 1042
/** \} */

View File

@ -36,6 +36,7 @@ struct Base;
struct BoundBox;
struct Curve;
struct Depsgraph;
struct GeometrySet;
struct GpencilModifierData;
struct HookGpencilModifierData;
struct HookModifierData;
@ -71,6 +72,8 @@ void BKE_object_free_curve_cache(struct Object *ob);
void BKE_object_free_derived_caches(struct Object *ob);
void BKE_object_free_caches(struct Object *object);
void BKE_object_set_preview_geometry_set(struct Object *ob, struct GeometrySet *geometry_set);
void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd);
void BKE_object_modifier_gpencil_hook_reset(struct Object *ob,
struct HookGpencilModifierData *hmd);

View File

@ -355,6 +355,7 @@ void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size, float detail_
typedef enum {
PBVH_Subdivide = 1,
PBVH_Collapse = 2,
PBVH_Cleanup = 4, //dissolve verts surrounded by either 3 or 4 triangles then triangulate
} PBVHTopologyUpdateMode;
bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
PBVHTopologyUpdateMode mode,

View File

@ -78,16 +78,17 @@ extern void (*BKE_volume_batch_cache_free_cb)(struct Volume *volume);
typedef struct VolumeGrid VolumeGrid;
bool BKE_volume_load(struct Volume *volume, struct Main *bmain);
bool BKE_volume_load(const struct Volume *volume, const struct Main *bmain);
void BKE_volume_unload(struct Volume *volume);
bool BKE_volume_is_loaded(const struct Volume *volume);
int BKE_volume_num_grids(const struct Volume *volume);
const char *BKE_volume_grids_error_msg(const struct Volume *volume);
const char *BKE_volume_grids_frame_filepath(const struct Volume *volume);
VolumeGrid *BKE_volume_grid_get(const struct Volume *volume, int grid_index);
VolumeGrid *BKE_volume_grid_active_get(const struct Volume *volume);
VolumeGrid *BKE_volume_grid_find(const struct Volume *volume, const char *name);
const VolumeGrid *BKE_volume_grid_get_for_read(const struct Volume *volume, int grid_index);
VolumeGrid *BKE_volume_grid_get_for_write(struct Volume *volume, int grid_index);
const VolumeGrid *BKE_volume_grid_active_get_for_read(const struct Volume *volume);
const VolumeGrid *BKE_volume_grid_find_for_read(const struct Volume *volume, const char *name);
/* Grid
*
@ -109,8 +110,8 @@ typedef enum VolumeGridType {
VOLUME_GRID_POINTS,
} VolumeGridType;
bool BKE_volume_grid_load(const struct Volume *volume, struct VolumeGrid *grid);
void BKE_volume_grid_unload(const struct Volume *volume, struct VolumeGrid *grid);
bool BKE_volume_grid_load(const struct Volume *volume, const struct VolumeGrid *grid);
void BKE_volume_grid_unload(const struct Volume *volume, const struct VolumeGrid *grid);
bool BKE_volume_grid_is_loaded(const struct VolumeGrid *grid);
/* Metadata */
@ -119,9 +120,6 @@ VolumeGridType BKE_volume_grid_type(const struct VolumeGrid *grid);
int BKE_volume_grid_channels(const struct VolumeGrid *grid);
void BKE_volume_grid_transform_matrix(const struct VolumeGrid *grid, float mat[4][4]);
/* Bounds */
bool BKE_volume_grid_bounds(const struct VolumeGrid *grid, float min[3], float max[3]);
/* Volume Editing
*
* These are intended for modifiers to use on evaluated datablocks.
@ -145,8 +143,8 @@ int BKE_volume_simplify_level(const struct Depsgraph *depsgraph);
float BKE_volume_simplify_factor(const struct Depsgraph *depsgraph);
/* File Save */
bool BKE_volume_save(struct Volume *volume,
struct Main *bmain,
bool BKE_volume_save(const struct Volume *volume,
const struct Main *bmain,
struct ReportList *reports,
const char *filepath);
@ -159,13 +157,26 @@ bool BKE_volume_save(struct Volume *volume,
* Access to OpenVDB grid for C++. These will automatically load grids from
* file or copy shared grids to make them writeable. */
#if defined(__cplusplus) && defined(WITH_OPENVDB)
# include <openvdb/openvdb.h>
# include <openvdb/points/PointDataGrid.h>
#ifdef __cplusplus
# include "BLI_float3.hh"
# include "BLI_float4x4.hh"
bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max);
# ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
# include <openvdb/points/PointDataGrid.h>
bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid,
blender::float3 &r_min,
blender::float3 &r_max);
openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid,
const blender::float4x4 &transform);
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const struct VolumeGrid *grid);
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const struct Volume *volume,
struct VolumeGrid *grid);
const struct VolumeGrid *grid);
openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const struct Volume *volume,
struct VolumeGrid *grid,
const bool clear);
@ -212,4 +223,5 @@ openvdb::GridBase::Ptr BKE_volume_grid_create_with_changed_resolution(
const openvdb::GridBase &old_grid,
const float resolution_factor);
# endif
#endif

View File

@ -43,7 +43,7 @@ typedef struct DenseFloatVolumeGrid {
} DenseFloatVolumeGrid;
bool BKE_volume_grid_dense_floats(const struct Volume *volume,
struct VolumeGrid *volume_grid,
const struct VolumeGrid *volume_grid,
DenseFloatVolumeGrid *r_dense_grid);
void BKE_volume_dense_float_grid_clear(DenseFloatVolumeGrid *dense_grid);
@ -53,7 +53,7 @@ typedef void (*BKE_volume_wireframe_cb)(
void *userdata, float (*verts)[3], int (*edges)[2], int totvert, int totedge);
void BKE_volume_grid_wireframe(const struct Volume *volume,
struct VolumeGrid *volume_grid,
const struct VolumeGrid *volume_grid,
BKE_volume_wireframe_cb cb,
void *cb_userdata);
@ -63,7 +63,7 @@ typedef void (*BKE_volume_selection_surface_cb)(
void *userdata, float (*verts)[3], int (*tris)[3], int totvert, int tottris);
void BKE_volume_grid_selection_surface(const struct Volume *volume,
struct VolumeGrid *volume_grid,
const struct VolumeGrid *volume_grid,
BKE_volume_selection_surface_cb cb,
void *cb_userdata);

View File

@ -287,8 +287,10 @@ bool BKE_animdata_id_is_animated(const struct ID *id)
!BLI_listbase_is_empty(&adt->overrides);
}
/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
* `IDTypeInfo` structure). */
/**
* Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
* `IDTypeInfo` structure).
*/
void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data)
{
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {

View File

@ -42,157 +42,192 @@ static CLG_LogRef LOG = {"bke.anim"};
/* ******************************************************************** */
/* Curve Paths - for curve deforms and/or curve following */
/**
* Free curve path data
*
* \note Frees the path itself!
* \note This is increasingly inaccurate with non-uniform #BevPoint subdivisions T24633.
*/
void free_path(Path *path)
static int get_bevlist_seg_array_size(const BevList *bl)
{
if (path->data) {
MEM_freeN(path->data);
if (bl->poly >= 0) {
/* Cyclic curve. */
return bl->nr;
}
MEM_freeN(path);
return bl->nr - 1;
}
/**
* Calculate a curve-deform path for a curve
* - Only called from displist.c -> #do_makeDispListCurveTypes
*/
void calc_curvepath(Object *ob, ListBase *nurbs)
int BKE_anim_path_get_array_size(const CurveCache *curve_cache)
{
BevList *bl;
BevPoint *bevp, *bevpn, *bevpfirst, *bevplast;
PathPoint *pp;
Nurb *nu;
Path *path;
float *fp, *dist, *maxdist, xyz[3];
float fac, d = 0, fac1, fac2;
int a, tot, cycl = 0;
BLI_assert(curve_cache != NULL);
/* in a path vertices are with equal differences: path->len = number of verts */
/* NOW WITH BEVELCURVE!!! */
BevList *bl = curve_cache->bev.first;
BLI_assert(bl != NULL && bl->nr > 1);
return get_bevlist_seg_array_size(bl);
}
float BKE_anim_path_get_length(const CurveCache *curve_cache)
{
const int seg_size = BKE_anim_path_get_array_size(curve_cache);
return curve_cache->anim_path_accum_length[seg_size - 1];
}
void BKE_anim_path_calc_data(Object *ob)
{
if (ob == NULL || ob->type != OB_CURVE) {
return;
}
if (ob->runtime.curve_cache->path) {
free_path(ob->runtime.curve_cache->path);
if (ob->runtime.curve_cache == NULL) {
CLOG_WARN(&LOG, "No curve cache!");
return;
}
ob->runtime.curve_cache->path = NULL;
/* weak! can only use first curve */
bl = ob->runtime.curve_cache->bev.first;
/* We only use the first curve. */
BevList *bl = ob->runtime.curve_cache->bev.first;
if (bl == NULL || !bl->nr) {
CLOG_WARN(&LOG, "No bev list data!");
return;
}
nu = nurbs->first;
ob->runtime.curve_cache->path = path = MEM_callocN(sizeof(Path), "calc_curvepath");
/* if POLY: last vertice != first vertice */
cycl = (bl->poly != -1);
tot = cycl ? bl->nr : bl->nr - 1;
path->len = tot + 1;
/* Exception: vector handle paths and polygon paths should be subdivided
* at least a factor resolution. */
if (path->len < nu->resolu * SEGMENTSU(nu)) {
path->len = nu->resolu * SEGMENTSU(nu);
/* Free old data. */
if (ob->runtime.curve_cache->anim_path_accum_length) {
MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
dist = (float *)MEM_mallocN(sizeof(float) * (tot + 1), "calcpathdist");
/* We assume that we have at least two points.
* If there is less than two points in the curve,
* no BevList should have been generated.
*/
BLI_assert(bl->nr > 1);
/* all lengths in *dist */
bevp = bevpfirst = bl->bevpoints;
fp = dist;
*fp = 0.0f;
for (a = 0; a < tot; a++) {
fp++;
if (cycl && a == tot - 1) {
sub_v3_v3v3(xyz, bevpfirst->vec, bevp->vec);
}
else {
sub_v3_v3v3(xyz, (bevp + 1)->vec, bevp->vec);
}
const int seg_size = get_bevlist_seg_array_size(bl);
float *len_data = (float *)MEM_mallocN(sizeof(float) * seg_size, "calcpathdist");
ob->runtime.curve_cache->anim_path_accum_length = len_data;
*fp = *(fp - 1) + len_v3(xyz);
bevp++;
BevPoint *bp_arr = bl->bevpoints;
float prev_len = 0.0f;
for (int i = 0; i < bl->nr - 1; i++) {
prev_len += len_v3v3(bp_arr[i].vec, bp_arr[i + 1].vec);
len_data[i] = prev_len;
}
path->totdist = *fp;
/* the path verts in path->data */
/* now also with TILT value */
pp = path->data = (PathPoint *)MEM_callocN(sizeof(PathPoint) * path->len, "pathdata");
bevp = bevpfirst;
bevpn = bevp + 1;
bevplast = bevpfirst + (bl->nr - 1);
if (UNLIKELY(bevpn > bevplast)) {
bevpn = cycl ? bevpfirst : bevplast;
if (bl->poly >= 0) {
/* Cyclic curve. */
len_data[seg_size - 1] = prev_len + len_v3v3(bp_arr[0].vec, bp_arr[bl->nr - 1].vec);
}
fp = dist + 1;
maxdist = dist + tot;
fac = 1.0f / ((float)path->len - 1.0f);
fac = fac * path->totdist;
for (a = 0; a < path->len; a++) {
d = ((float)a) * fac;
/* we're looking for location (distance) 'd' in the array */
if (LIKELY(tot > 0)) {
while ((fp < maxdist) && (d >= *fp)) {
fp++;
if (bevp < bevplast) {
bevp++;
}
bevpn = bevp + 1;
if (UNLIKELY(bevpn > bevplast)) {
bevpn = cycl ? bevpfirst : bevplast;
}
}
fac1 = (*(fp)-d) / (*(fp) - *(fp - 1));
fac2 = 1.0f - fac1;
}
else {
fac1 = 1.0f;
fac2 = 0.0f;
}
interp_v3_v3v3(pp->vec, bevp->vec, bevpn->vec, fac2);
pp->vec[3] = fac1 * bevp->tilt + fac2 * bevpn->tilt;
pp->radius = fac1 * bevp->radius + fac2 * bevpn->radius;
pp->weight = fac1 * bevp->weight + fac2 * bevpn->weight;
interp_qt_qtqt(pp->quat, bevp->quat, bevpn->quat, fac2);
normalize_qt(pp->quat);
pp++;
}
MEM_freeN(dist);
}
static int interval_test(const int min, const int max, int p1, const int cycl)
static void get_curve_points_from_idx(const int idx,
const BevList *bl,
const bool is_cyclic,
BevPoint const **r_p0,
BevPoint const **r_p1,
BevPoint const **r_p2,
BevPoint const **r_p3)
{
if (cycl) {
p1 = mod_i(p1 - min, (max - min + 1)) + min;
}
else {
if (p1 < min) {
p1 = min;
BLI_assert(idx >= 0);
BLI_assert(idx < bl->nr - 1 || (is_cyclic && idx < bl->nr));
BLI_assert(bl->nr > 1);
const BevPoint *bp_arr = bl->bevpoints;
/* First segment. */
if (idx == 0) {
*r_p1 = &bp_arr[0];
if (is_cyclic) {
*r_p0 = &bp_arr[bl->nr - 1];
}
else if (p1 > max) {
p1 = max;
else {
*r_p0 = *r_p1;
}
*r_p2 = &bp_arr[1];
if (bl->nr > 2) {
*r_p3 = &bp_arr[2];
}
else {
*r_p3 = *r_p2;
}
return;
}
/* Last segment (or next to last in a cyclic curve). */
if (idx == bl->nr - 2) {
/* The case when the bl->nr == 2 falls in to the "first segment" check above.
* So here we can assume that bl->nr > 2.
*/
*r_p0 = &bp_arr[idx - 1];
*r_p1 = &bp_arr[idx];
*r_p2 = &bp_arr[idx + 1];
if (is_cyclic) {
*r_p3 = &bp_arr[0];
}
else {
*r_p3 = *r_p2;
}
return;
}
if (idx == bl->nr - 1) {
/* Last segment in a cyclic curve. This should only trigger if the curve is cyclic
* as it gets an extra segment between the end and the start point. */
*r_p0 = &bp_arr[idx - 1];
*r_p1 = &bp_arr[idx];
*r_p2 = &bp_arr[0];
*r_p3 = &bp_arr[1];
return;
}
/* To get here the curve has to have four curve points or more and idx can't
* be the first or the last segment.
* So we can assume that we can get four points without any special checks.
*/
*r_p0 = &bp_arr[idx - 1];
*r_p1 = &bp_arr[idx];
*r_p2 = &bp_arr[idx + 1];
*r_p3 = &bp_arr[idx + 2];
}
static bool binary_search_anim_path(const float *accum_len_arr,
const int seg_size,
const float goal_len,
int *r_idx,
float *r_frac)
{
float left_len, right_len;
int cur_idx = 0, cur_base = 0;
int cur_step = seg_size - 1;
while (true) {
cur_idx = cur_base + cur_step / 2;
left_len = accum_len_arr[cur_idx];
right_len = accum_len_arr[cur_idx + 1];
if (left_len <= goal_len && right_len > goal_len) {
*r_idx = cur_idx + 1;
*r_frac = (goal_len - left_len) / (right_len - left_len);
return true;
}
if (cur_idx == 0) {
/* We ended up at the first segment. The point must be in here. */
*r_idx = 0;
*r_frac = goal_len / accum_len_arr[0];
return true;
}
if (UNLIKELY(cur_step == 0)) {
/* This should never happen unless there is something horribly wrong. */
CLOG_ERROR(&LOG, "Couldn't find any valid point on the animation path!");
BLI_assert(!"Couldn't find any valid point on the animation path!");
return false;
}
if (left_len < goal_len) {
/* Go to the right. */
cur_base = cur_idx + 1;
cur_step--;
} /* Else, go to the left. */
cur_step /= 2;
}
return p1;
}
/**
@ -203,66 +238,70 @@ static int interval_test(const int min, const int max, int p1, const int cycl)
*
* \return success.
*/
bool where_on_path(const Object *ob,
float ctime,
float r_vec[4],
float r_dir[3],
float r_quat[4],
float *r_radius,
float *r_weight)
bool BKE_where_on_path(const Object *ob,
float ctime,
float r_vec[4],
float r_dir[3],
float r_quat[4],
float *r_radius,
float *r_weight)
{
Curve *cu;
const Nurb *nu;
const BevList *bl;
const Path *path;
const PathPoint *pp, *p0, *p1, *p2, *p3;
float fac;
float data[4];
int cycl = 0, s0, s1, s2, s3;
const ListBase *nurbs;
if (ob == NULL || ob->type != OB_CURVE) {
return false;
}
cu = ob->data;
if (ob->runtime.curve_cache == NULL || ob->runtime.curve_cache->path == NULL ||
ob->runtime.curve_cache->path->data == NULL) {
CLOG_WARN(&LOG, "no path!");
Curve *cu = ob->data;
if (ob->runtime.curve_cache == NULL) {
CLOG_WARN(&LOG, "No curve cache!");
return false;
}
path = ob->runtime.curve_cache->path;
pp = path->data;
/* test for cyclic */
bl = ob->runtime.curve_cache->bev.first;
if (!bl) {
/* We only use the first curve. */
BevList *bl = ob->runtime.curve_cache->bev.first;
if (bl == NULL || !bl->nr) {
CLOG_WARN(&LOG, "No bev list data!");
return false;
}
if (!bl->nr) {
return false;
}
if (bl->poly > -1) {
cycl = 1;
/* Test for cyclic curve. */
const bool is_cyclic = bl->poly >= 0;
if (is_cyclic) {
/* Wrap the time into a 0.0 - 1.0 range. */
if (ctime < 0.0f || ctime > 1.0f) {
ctime -= floorf(ctime);
}
}
/* values below zero for non-cyclic curves give strange results */
BLI_assert(cycl || ctime >= 0.0f);
/* The curve points for this ctime value. */
const BevPoint *p0, *p1, *p2, *p3;
ctime *= (path->len - 1);
float frac;
const int seg_size = get_bevlist_seg_array_size(bl);
const float *accum_len_arr = ob->runtime.curve_cache->anim_path_accum_length;
const float goal_len = ctime * accum_len_arr[seg_size - 1];
s1 = (int)floor(ctime);
fac = (float)(s1 + 1) - ctime;
/* Are we simply trying to get the start/end point? */
if (ctime <= 0.0f || ctime >= 1.0f) {
const float clamp_time = clamp_f(ctime, 0.0f, 1.0f);
const int idx = clamp_time * (seg_size - 1);
get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
/* path->len is corrected for cyclic */
s0 = interval_test(0, path->len - 1 - cycl, s1 - 1, cycl);
s1 = interval_test(0, path->len - 1 - cycl, s1, cycl);
s2 = interval_test(0, path->len - 1 - cycl, s1 + 1, cycl);
s3 = interval_test(0, path->len - 1 - cycl, s1 + 2, cycl);
if (idx == 0) {
frac = goal_len / accum_len_arr[0];
}
else {
frac = (goal_len - accum_len_arr[idx - 1]) / (accum_len_arr[idx] - accum_len_arr[idx - 1]);
}
}
else {
/* Do binary search to get the correct segment. */
int idx;
const bool found_idx = binary_search_anim_path(accum_len_arr, seg_size, goal_len, &idx, &frac);
p0 = pp + s0;
p1 = pp + s1;
p2 = pp + s2;
p3 = pp + s3;
if (UNLIKELY(!found_idx)) {
return false;
}
get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
}
/* NOTE: commented out for follow constraint
*
@ -272,65 +311,68 @@ bool where_on_path(const Object *ob,
*/
// if (cu->flag & CU_FOLLOW) {
key_curve_tangent_weights(1.0f - fac, data, KEY_BSPLINE);
float w[4];
interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, data);
key_curve_tangent_weights(frac, w, KEY_BSPLINE);
interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
/* Make compatible with #vec_to_quat. */
negate_v3(r_dir);
//}
nurbs = BKE_curve_editNurbs_get(cu);
const ListBase *nurbs = BKE_curve_editNurbs_get(cu);
if (!nurbs) {
nurbs = &cu->nurb;
}
nu = nurbs->first;
const Nurb *nu = nurbs->first;
/* make sure that first and last frame are included in the vectors here */
if (nu->type == CU_POLY) {
key_curve_position_weights(1.0f - fac, data, KEY_LINEAR);
if (ELEM(nu->type, CU_POLY, CU_BEZIER, CU_NURBS)) {
key_curve_position_weights(frac, w, KEY_LINEAR);
}
else if (nu->type == CU_BEZIER) {
key_curve_position_weights(1.0f - fac, data, KEY_LINEAR);
}
else if (s0 == s1 || p2 == p3) {
key_curve_position_weights(1.0f - fac, data, KEY_CARDINAL);
else if (p2 == p3) {
key_curve_position_weights(frac, w, KEY_CARDINAL);
}
else {
key_curve_position_weights(1.0f - fac, data, KEY_BSPLINE);
key_curve_position_weights(frac, w, KEY_BSPLINE);
}
r_vec[0] = /* X */
data[0] * p0->vec[0] + data[1] * p1->vec[0] + data[2] * p2->vec[0] + data[3] * p3->vec[0];
w[0] * p0->vec[0] + w[1] * p1->vec[0] + w[2] * p2->vec[0] + w[3] * p3->vec[0];
r_vec[1] = /* Y */
data[0] * p0->vec[1] + data[1] * p1->vec[1] + data[2] * p2->vec[1] + data[3] * p3->vec[1];
w[0] * p0->vec[1] + w[1] * p1->vec[1] + w[2] * p2->vec[1] + w[3] * p3->vec[1];
r_vec[2] = /* Z */
data[0] * p0->vec[2] + data[1] * p1->vec[2] + data[2] * p2->vec[2] + data[3] * p3->vec[2];
r_vec[3] = /* Tilt, should not be needed since we have quat still used */
data[0] * p0->vec[3] + data[1] * p1->vec[3] + data[2] * p2->vec[3] + data[3] * p3->vec[3];
w[0] * p0->vec[2] + w[1] * p1->vec[2] + w[2] * p2->vec[2] + w[3] * p3->vec[2];
/* Clamp weights to 0-1 as we don't want to extrapolate other values than position. */
clamp_v4(w, 0.0f, 1.0f);
/* Tilt, should not be needed since we have quat still used. */
r_vec[3] = w[0] * p0->tilt + w[1] * p1->tilt + w[2] * p2->tilt + w[3] * p3->tilt;
if (r_quat) {
float totfac, q1[4], q2[4];
totfac = data[0] + data[3];
totfac = w[0] + w[3];
if (totfac > FLT_EPSILON) {
interp_qt_qtqt(q1, p0->quat, p3->quat, data[3] / totfac);
interp_qt_qtqt(q1, p0->quat, p3->quat, w[3] / totfac);
}
else {
copy_qt_qt(q1, p1->quat);
}
totfac = data[1] + data[2];
totfac = w[1] + w[2];
if (totfac > FLT_EPSILON) {
interp_qt_qtqt(q2, p1->quat, p2->quat, data[2] / totfac);
interp_qt_qtqt(q2, p1->quat, p2->quat, w[2] / totfac);
}
else {
copy_qt_qt(q2, p3->quat);
}
totfac = data[0] + data[1] + data[2] + data[3];
totfac = w[0] + w[1] + w[2] + w[3];
if (totfac > FLT_EPSILON) {
interp_qt_qtqt(r_quat, q1, q2, (data[1] + data[2]) / totfac);
interp_qt_qtqt(r_quat, q1, q2, (w[1] + w[2]) / totfac);
}
else {
copy_qt_qt(r_quat, q2);
@ -338,13 +380,11 @@ bool where_on_path(const Object *ob,
}
if (r_radius) {
*r_radius = data[0] * p0->radius + data[1] * p1->radius + data[2] * p2->radius +
data[3] * p3->radius;
*r_radius = w[0] * p0->radius + w[1] * p1->radius + w[2] * p2->radius + w[3] * p3->radius;
}
if (r_weight) {
*r_weight = data[0] * p0->weight + data[1] * p1->weight + data[2] * p2->weight +
data[3] * p3->weight;
*r_weight = w[0] * p0->weight + w[1] * p1->weight + w[2] * p2->weight + w[3] * p3->weight;
}
return true;

View File

@ -1605,8 +1605,9 @@ static bool nla_combine_get_inverted_strip_value(const int mix_mode,
}
}
/** Accumulate quaternion channels for Combine mode according to influence.
* \returns blended_value = lower_values @ strip_values^infl
/**
* Accumulate quaternion channels for Combine mode according to influence.
* \returns `blended_value = lower_values @ strip_values^infl`
*/
static void nla_combine_quaternion(const float lower_values[4],
const float strip_values[4],
@ -2094,8 +2095,10 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels,
/* ---------------------- */
/** Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored
* and includes a workaround for when user is not editing in place. */
/**
* Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored
* and includes a workaround for when user is not editing in place.
*/
static void animsys_create_tweak_strip(const AnimData *adt,
const bool keyframing_to_strip,
NlaStrip *r_tweak_strip)
@ -2210,8 +2213,10 @@ static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt)
return true;
}
/** Check for special case of non-pushed action being evaluated with no NLA influence (off and no
* strips evaluated) nor NLA interference (ensure NLA not soloing). */
/**
* Check for special case of non-pushed action being evaluated with no NLA influence (off and no
* strips evaluated) nor NLA interference (ensure NLA not soloing).
*/
static bool is_action_track_evaluated_without_nla(const AnimData *adt,
const bool any_strip_evaluated)
{
@ -2491,7 +2496,8 @@ void nlasnapshot_ensure_channels(NlaEvalData *eval_data, NlaEvalSnapshot *snapsh
}
}
/** Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
/**
* Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
* to the given \a upper_blendmode and \a upper_influence.
*
* For \a upper_snapshot, blending limited to values in the \a blend_domain. For Replace blendmode,

View File

@ -229,7 +229,7 @@ static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *st
CurveCache *cache = ikData->tar->runtime.curve_cache;
if (ELEM(NULL, cache, cache->path, cache->path->data)) {
if (ELEM(NULL, cache, cache->anim_path_accum_length)) {
return false;
}
@ -242,7 +242,7 @@ static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *st
if ((ikData->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) && (tree->totlength != 0.0f)) {
/* get the current length of the curve */
/* NOTE: this is assumed to be correct even after the curve was resized */
float splineLen = cache->path->totdist;
const float splineLen = BKE_anim_path_get_length(cache);
/* calculate the scale factor to multiply all the path values by so that the
* bone chain retains its current length, such that
@ -297,7 +297,7 @@ static void splineik_evaluate_bone(
}
/* tail endpoint */
if (where_on_path(ikData->tar, pointEnd, vec, dir, NULL, &rad, NULL)) {
if (BKE_where_on_path(ikData->tar, pointEnd, vec, dir, NULL, &rad, NULL)) {
/* apply curve's object-mode transforms to the position
* unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
*/
@ -314,7 +314,7 @@ static void splineik_evaluate_bone(
}
/* head endpoint */
if (where_on_path(ikData->tar, pointStart, vec, dir, NULL, &rad, NULL)) {
if (BKE_where_on_path(ikData->tar, pointStart, vec, dir, NULL, &rad, NULL)) {
/* apply curve's object-mode transforms to the position
* unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
*/

View File

@ -822,12 +822,16 @@ Set<std::string> GeometryComponent::attribute_names() const
return attributes;
}
void GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const
/**
* \return False if the callback explicitly returned false at any point, otherwise true,
* meaning the callback made it all the way through.
*/
bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
if (providers == nullptr) {
return;
return true;
}
/* Keep track handled attribute names to make sure that we do not return the same name twice. */
@ -838,7 +842,7 @@ void GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
if (provider->exists(*this)) {
AttributeMetaData meta_data{provider->domain(), provider->data_type()};
if (!callback(provider->name(), meta_data)) {
return;
return false;
}
handled_attribute_names.add_new(provider->name());
}
@ -852,9 +856,11 @@ void GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
return true;
});
if (!continue_loop) {
return;
return false;
}
}
return true;
}
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const

View File

@ -2667,4 +2667,13 @@ void BKE_brush_get_dyntopo(Brush *brush, Sculpt *sd, DynTopoSettings *out)
out->flag &= ~DYNTOPO_COLLAPSE;
}
}
if (inherit & DYNTOPO_CLEANUP) {
if (sd->flags & SCULPT_DYNTOPO_CLEANUP) {
out->flag |= DYNTOPO_CLEANUP;
}
else {
out->flag &= ~DYNTOPO_CLEANUP;
}
}
};

View File

@ -2016,12 +2016,10 @@ static void scene_collections_array(Scene *scene,
BLI_assert(collection != NULL);
scene_collection_callback(collection, scene_collections_count, r_collections_array_len);
if (*r_collections_array_len == 0) {
return;
}
BLI_assert(*r_collections_array_len > 0);
Collection **array = MEM_mallocN(sizeof(Collection *) * (*r_collections_array_len),
"CollectionArray");
Collection **array = MEM_malloc_arrayN(
*r_collections_array_len, sizeof(Collection *), "CollectionArray");
*r_collections_array = array;
scene_collection_callback(collection, scene_collections_build_array, &array);
}
@ -2036,8 +2034,9 @@ void BKE_scene_collections_iterator_begin(BLI_Iterator *iter, void *data_in)
CollectionsIteratorData *data = MEM_callocN(sizeof(CollectionsIteratorData), __func__);
data->scene = scene;
BLI_ITERATOR_INIT(iter);
iter->data = data;
iter->valid = true;
scene_collections_array(scene, (Collection ***)&data->array, &data->tot);
BLI_assert(data->tot != 0);
@ -2079,16 +2078,22 @@ typedef struct SceneObjectsIteratorData {
BLI_Iterator scene_collection_iter;
} SceneObjectsIteratorData;
void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
static void scene_objects_iterator_begin(BLI_Iterator *iter, Scene *scene, GSet *visited_objects)
{
Scene *scene = data_in;
SceneObjectsIteratorData *data = MEM_callocN(sizeof(SceneObjectsIteratorData), __func__);
BLI_ITERATOR_INIT(iter);
iter->data = data;
/* lookup list ot make sure each object is object called once */
data->visited = BLI_gset_ptr_new(__func__);
/* Lookup list to make sure that each object is only processed once. */
if (visited_objects != NULL) {
data->visited = visited_objects;
}
else {
data->visited = BLI_gset_ptr_new(__func__);
}
/* we wrap the scenecollection iterator here to go over the scene collections */
/* We wrap the scenecollection iterator here to go over the scene collections. */
BKE_scene_collections_iterator_begin(&data->scene_collection_iter, scene);
Collection *collection = data->scene_collection_iter.current;
@ -2097,6 +2102,13 @@ void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
BKE_scene_objects_iterator_next(iter);
}
void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
{
Scene *scene = data_in;
scene_objects_iterator_begin(iter, scene, NULL);
}
/**
* Ensures we only get each object once, even when included in several collections.
*/
@ -2150,9 +2162,36 @@ void BKE_scene_objects_iterator_end(BLI_Iterator *iter)
SceneObjectsIteratorData *data = iter->data;
if (data) {
BKE_scene_collections_iterator_end(&data->scene_collection_iter);
BLI_gset_free(data->visited, NULL);
if (data->visited != NULL) {
BLI_gset_free(data->visited, NULL);
}
MEM_freeN(data);
}
}
/**
* Generate a new GSet (or extend given `objects_gset` if not NULL) with all objects referenced by
* all collections of given `scene`.
*
* \note: This will include objects without a base currently (because they would belong to excluded
* collections only e.g.).
*/
GSet *BKE_scene_objects_as_gset(Scene *scene, GSet *objects_gset)
{
BLI_Iterator iter;
scene_objects_iterator_begin(&iter, scene, objects_gset);
while (iter.valid) {
BKE_scene_objects_iterator_next(&iter);
}
/* `return_gset` is either given `objects_gset` (if non-NULL), or the GSet allocated by the
* iterator. Either way, we want to get it back, and prevent `BKE_scene_objects_iterator_end`
* from freeing it. */
GSet *return_gset = ((SceneObjectsIteratorData *)iter.data)->visited;
((SceneObjectsIteratorData *)iter.data)->visited = NULL;
BKE_scene_objects_iterator_end(&iter);
return return_gset;
}
/** \} */

View File

@ -1463,12 +1463,10 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
* currently for paths to work it needs to go through the bevlist/displist system (ton)
*/
if (ct->tar->runtime.curve_cache && ct->tar->runtime.curve_cache->path &&
ct->tar->runtime.curve_cache->path->data) {
if (ct->tar->runtime.curve_cache && ct->tar->runtime.curve_cache->anim_path_accum_length) {
float quat[4];
if ((data->followflag & FOLLOWPATH_STATIC) == 0) {
/* animated position along curve depending on time */
Nurb *nu = cu->nurb.first;
curvetime = cu->ctime - data->offset;
/* ctime is now a proper var setting of Curve which gets set by Animato like any other var
@ -1477,31 +1475,19 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
* we divide the curvetime calculated in the previous step by the length of the path,
* to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. */
curvetime /= cu->pathlen;
if (nu && nu->flagu & CU_NURB_CYCLIC) {
/* If the curve is cyclic, enable looping around if the time is
* outside the bounds 0..1 */
if ((curvetime < 0.0f) || (curvetime > 1.0f)) {
curvetime -= floorf(curvetime);
}
}
else {
/* The curve is not cyclic, so clamp to the begin/end points. */
CLAMP(curvetime, 0.0f, 1.0f);
}
}
else {
/* fixed position along curve */
curvetime = data->offset_fac;
}
if (where_on_path(ct->tar,
curvetime,
vec,
dir,
(data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL,
&radius,
NULL)) { /* quat_pt is quat or NULL*/
if (BKE_where_on_path(ct->tar,
curvetime,
vec,
dir,
(data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL,
&radius,
NULL)) { /* quat_pt is quat or NULL*/
float totmat[4][4];
unit_m4(totmat);
@ -3784,8 +3770,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
BKE_object_minmax(ct->tar, curveMin, curveMax, true);
/* get targetmatrix */
if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->path &&
data->tar->runtime.curve_cache->path->data) {
if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->anim_path_accum_length) {
float vec[4], dir[3], totmat[4][4];
float curvetime;
short clamp_axis;
@ -3869,7 +3854,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
}
/* 3. position on curve */
if (where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) {
if (BKE_where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) {
unit_m4(totmat);
copy_v3_v3(totmat[3], vec);

View File

@ -54,7 +54,7 @@ struct CryptomatteSession {
/* Layer names in order of creation. */
blender::Vector<std::string> layer_names;
CryptomatteSession();
CryptomatteSession() = default;
CryptomatteSession(const Main *bmain);
CryptomatteSession(StampData *stamp_data);
CryptomatteSession(const Scene *scene);
@ -67,10 +67,6 @@ struct CryptomatteSession {
#endif
};
CryptomatteSession::CryptomatteSession()
{
}
CryptomatteSession::CryptomatteSession(const Main *bmain)
{
if (!BLI_listbase_is_empty(&bmain->objects)) {

View File

@ -68,75 +68,7 @@ static void init_curve_deform(const Object *ob_curve, const Object *ob_target, C
}
/**
* This makes sure we can extend for non-cyclic.
*
* \return Success.
*/
static bool where_on_path_deform(const Object *ob_curve,
float ctime,
float r_vec[4],
float r_dir[3],
float r_quat[4],
float *r_radius)
{
BevList *bl;
float ctime1;
int cycl = 0;
/* test for cyclic */
bl = ob_curve->runtime.curve_cache->bev.first;
if (!bl->nr) {
return false;
}
if (bl->poly > -1) {
cycl = 1;
}
if (cycl == 0) {
ctime1 = CLAMPIS(ctime, 0.0f, 1.0f);
}
else {
ctime1 = ctime;
}
/* vec needs 4 items */
if (where_on_path(ob_curve, ctime1, r_vec, r_dir, r_quat, r_radius, NULL)) {
if (cycl == 0) {
Path *path = ob_curve->runtime.curve_cache->path;
float dvec[3];
if (ctime < 0.0f) {
sub_v3_v3v3(dvec, path->data[1].vec, path->data[0].vec);
mul_v3_fl(dvec, ctime * (float)path->len);
add_v3_v3(r_vec, dvec);
if (r_quat) {
copy_qt_qt(r_quat, path->data[0].quat);
}
if (r_radius) {
*r_radius = path->data[0].radius;
}
}
else if (ctime > 1.0f) {
sub_v3_v3v3(dvec, path->data[path->len - 1].vec, path->data[path->len - 2].vec);
mul_v3_fl(dvec, (ctime - 1.0f) * (float)path->len);
add_v3_v3(r_vec, dvec);
if (r_quat) {
copy_qt_qt(r_quat, path->data[path->len - 1].quat);
}
if (r_radius) {
*r_radius = path->data[path->len - 1].radius;
}
/* weight - not used but could be added */
}
}
return true;
}
return false;
}
/**
* For each point, rotate & translate to curve use path, since it has constant distances.
* For each point, rotate & translate to curve.
*
* \param co: local coord, result local too.
* \param r_quat: returns quaternion for rotation,
@ -155,7 +87,7 @@ static bool calc_curve_deform(
return false;
}
if (ob_curve->runtime.curve_cache->path == NULL) {
if (ob_curve->runtime.curve_cache->anim_path_accum_length == NULL) {
return false; /* happens on append, cyclic dependencies and empty curves */
}
@ -172,8 +104,10 @@ static bool calc_curve_deform(
}
}
else {
if (LIKELY(ob_curve->runtime.curve_cache->path->totdist > FLT_EPSILON)) {
fac = -(co[index] - cd->dmax[index]) / (ob_curve->runtime.curve_cache->path->totdist);
CurveCache *cc = ob_curve->runtime.curve_cache;
float totdist = BKE_anim_path_get_length(cc);
if (LIKELY(totdist > FLT_EPSILON)) {
fac = -(co[index] - cd->dmax[index]) / totdist;
}
else {
fac = 0.0f;
@ -192,8 +126,10 @@ static bool calc_curve_deform(
}
}
else {
if (LIKELY(ob_curve->runtime.curve_cache->path->totdist > FLT_EPSILON)) {
fac = +(co[index] - cd->dmin[index]) / (ob_curve->runtime.curve_cache->path->totdist);
CurveCache *cc = ob_curve->runtime.curve_cache;
float totdist = BKE_anim_path_get_length(cc);
if (LIKELY(totdist > FLT_EPSILON)) {
fac = +(co[index] - cd->dmin[index]) / totdist;
}
else {
fac = 0.0f;
@ -201,7 +137,7 @@ static bool calc_curve_deform(
}
}
if (where_on_path_deform(ob_curve, fac, loc, dir, new_quat, &radius)) { /* returns OK */
if (BKE_where_on_path(ob_curve, fac, loc, dir, new_quat, &radius, NULL)) { /* returns OK */
float quat[4], cent[3];
if (cd->no_rot_axis) { /* set by caller */

View File

@ -1491,10 +1491,10 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
* was needed before and only not needed for orco calculation.
*/
if (!for_orco) {
if (ob->runtime.curve_cache->path) {
free_path(ob->runtime.curve_cache->path);
if (ob->runtime.curve_cache->anim_path_accum_length) {
MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
ob->runtime.curve_cache->path = NULL;
ob->runtime.curve_cache->anim_path_accum_length = NULL;
}
if (ob->type == OB_FONT) {
@ -1703,7 +1703,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
if (!for_orco) {
if ((cu->flag & CU_PATH) ||
DEG_get_eval_flags_for_id(depsgraph, &ob->id) & DAG_EVAL_NEED_CURVE_PATH) {
calc_curvepath(ob, &nubase);
BKE_anim_path_calc_data(ob);
}
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);

View File

@ -161,13 +161,13 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef
if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVE) {
Curve *cu = eff->ob->data;
if (cu->flag & CU_PATH) {
if (eff->ob->runtime.curve_cache == NULL || eff->ob->runtime.curve_cache->path == NULL ||
eff->ob->runtime.curve_cache->path->data == NULL) {
if (eff->ob->runtime.curve_cache == NULL ||
eff->ob->runtime.curve_cache->anim_path_accum_length == NULL) {
BKE_displist_make_curveTypes(depsgraph, eff->scene, eff->ob, false, false);
}
if (eff->ob->runtime.curve_cache->path && eff->ob->runtime.curve_cache->path->data) {
where_on_path(
if (eff->ob->runtime.curve_cache->anim_path_accum_length) {
BKE_where_on_path(
eff->ob, 0.0, eff->guide_loc, eff->guide_dir, NULL, &eff->guide_radius, NULL);
mul_m4_v3(eff->ob->obmat, eff->guide_loc);
mul_mat3_m4_v3(eff->ob->obmat, eff->guide_dir);

View File

@ -181,8 +181,10 @@ void BKE_fcurves_copy(ListBase *dst, ListBase *src)
}
}
/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
* `IDTypeInfo` structure). */
/**
* Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
* `IDTypeInfo` structure).
*/
void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data)
{
ChannelDriver *driver = fcu->driver;
@ -1508,7 +1510,8 @@ bool test_time_fcurve(FCurve *fcu)
/** \name F-Curve Calculations
* \{ */
/* The length of each handle is not allowed to be more
/**
* The length of each handle is not allowed to be more
* than the horizontal distance between (v1-v4).
* This is to prevent curve loops.
*
@ -1555,9 +1558,13 @@ void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], con
}
}
/** Find roots of cubic equation (c0 x³ + c1 x² + c2 x + c3)
/**
.
* Find roots of cubic equation (c0 x³ + c1 x² + c2 x + c3)
* \return number of roots in `o`.
* NOTE: it is up to the caller to allocate enough memory for `o`. */
*
* \note it is up to the caller to allocate enough memory for `o`.
*/
static int solve_cubic(double c0, double c1, double c2, double c3, float *o)
{
double a, b, c, p, q, d, t, phi;

View File

@ -1275,7 +1275,7 @@ static bool vfont_to_curve(Object *ob,
if (cu->textoncurve && cu->textoncurve->type == OB_CURVE) {
BLI_assert(cu->textoncurve->runtime.curve_cache != NULL);
if (cu->textoncurve->runtime.curve_cache != NULL &&
cu->textoncurve->runtime.curve_cache->path != NULL) {
cu->textoncurve->runtime.curve_cache->anim_path_accum_length != NULL) {
float distfac, imat[4][4], imat3[3][3], cmat[3][3];
float minx, maxx;
float timeofs, sizefac;
@ -1309,7 +1309,8 @@ static bool vfont_to_curve(Object *ob,
/* length correction */
const float chartrans_size_x = maxx - minx;
if (chartrans_size_x != 0.0f) {
const float totdist = cu->textoncurve->runtime.curve_cache->path->totdist;
const CurveCache *cc = cu->textoncurve->runtime.curve_cache;
const float totdist = BKE_anim_path_get_length(cc);
distfac = (sizefac * totdist) / chartrans_size_x;
distfac = (distfac > 1.0f) ? (1.0f / distfac) : 1.0f;
}
@ -1363,8 +1364,8 @@ static bool vfont_to_curve(Object *ob,
/* calc the right loc AND the right rot separately */
/* vec, tvec need 4 items */
where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
mul_v3_fl(vec, sizefac);

View File

@ -108,6 +108,18 @@ bool InstancesComponent::is_empty() const
return transforms_.size() == 0;
}
bool InstancesComponent::owns_direct_data() const
{
/* The object and collection instances are not direct data. Instance transforms are direct data
* and are always owned. Therefore, instance components always own all their direct data. */
return true;
}
void InstancesComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
}
static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
{
using namespace blender;

View File

@ -157,6 +157,20 @@ bool MeshComponent::is_empty() const
return mesh_ == nullptr;
}
bool MeshComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void MeshComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
ownership_ = GeometryOwnershipType::Owned;
}
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -107,6 +107,20 @@ bool PointCloudComponent::is_empty() const
return pointcloud_ == nullptr;
}
bool PointCloudComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void PointCloudComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
ownership_ = GeometryOwnershipType::Owned;
}
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -97,4 +97,18 @@ Volume *VolumeComponent::get_for_write()
return volume_;
}
bool VolumeComponent::owns_direct_data() const
{
return ownership_ == GeometryOwnershipType::Owned;
}
void VolumeComponent::ensure_owns_direct_data()
{
BLI_assert(this->is_mutable());
if (ownership_ != GeometryOwnershipType::Owned) {
volume_ = BKE_volume_copy_for_eval(volume_, false);
ownership_ = GeometryOwnershipType::Owned;
}
}
/** \} */

View File

@ -49,10 +49,6 @@ GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type)
{
}
GeometryComponent ::~GeometryComponent()
{
}
GeometryComponent *GeometryComponent::create(GeometryComponentType component_type)
{
switch (component_type) {
@ -182,6 +178,10 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (mesh != nullptr) {
BKE_mesh_wrapper_minmax(mesh, *r_min, *r_max);
}
const Volume *volume = this->get_volume_for_read();
if (volume != nullptr) {
BKE_volume_min_max(volume, *r_min, *r_max);
}
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
@ -211,6 +211,19 @@ void GeometrySet::clear()
components_.clear();
}
/* Make sure that the geometry can be cached. This does not ensure ownership of object/collection
* instances. */
void GeometrySet::ensure_owns_direct_data()
{
for (GeometryComponentType type : components_.keys()) {
const GeometryComponent *component = this->get_component_for_read(type);
if (!component->owns_direct_data()) {
GeometryComponent &component_for_write = this->get_component_for_write(type);
component_for_write.ensure_owns_direct_data();
}
}
}
/* Returns a read-only mesh or null. */
const Mesh *GeometrySet::get_mesh_for_read() const
{

View File

@ -162,10 +162,126 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set,
geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups);
}
void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
Span<GeometryComponentType> component_types,
Span<GeometryInstanceGroup> set_groups,
const Set<std::string> &ignored_attributes)
static bool collection_instance_attribute_foreach(const Collection &collection,
const AttributeForeachCallback callback,
const int limit,
int &count);
static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set,
const AttributeForeachCallback callback,
const int limit,
int &count);
static bool object_instance_attribute_foreach(const Object &object,
const AttributeForeachCallback callback,
const int limit,
int &count)
{
GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
if (!instances_attribute_foreach_recursive(instance_geometry_set, callback, limit, count)) {
return false;
}
if (object.type == OB_EMPTY) {
const Collection *collection_instance = object.instance_collection;
if (collection_instance != nullptr) {
if (!collection_instance_attribute_foreach(*collection_instance, callback, limit, count)) {
return false;
}
}
}
return true;
}
static bool collection_instance_attribute_foreach(const Collection &collection,
const AttributeForeachCallback callback,
const int limit,
int &count)
{
LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
BLI_assert(collection_object->ob != nullptr);
const Object &object = *collection_object->ob;
if (!object_instance_attribute_foreach(object, callback, limit, count)) {
return false;
}
}
LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
BLI_assert(collection_child->collection != nullptr);
const Collection &collection = *collection_child->collection;
if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
return false;
}
}
return true;
}
/**
* \return True if the recursive iteration should continue, false if the limit is reached or the
* callback has returned false indicating it should stop.
*/
static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set,
const AttributeForeachCallback callback,
const int limit,
int &count)
{
for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
if (!component->attribute_foreach(callback)) {
return false;
}
}
/* Now that this this geometry set is visited, increase the count and check with the limit. */
if (limit > 0 && count++ > limit) {
return false;
}
const InstancesComponent *instances_component =
geometry_set.get_component_for_read<InstancesComponent>();
if (instances_component == nullptr) {
return true;
}
for (const InstancedData &data : instances_component->instanced_data()) {
if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
BLI_assert(data.data.object != nullptr);
const Object &object = *data.data.object;
if (!object_instance_attribute_foreach(object, callback, limit, count)) {
return false;
}
}
else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
BLI_assert(data.data.collection != nullptr);
const Collection &collection = *data.data.collection;
if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
return false;
}
}
}
return true;
}
/**
* Call the callback on all of this geometry set's components, including geometry sets from
* instances and recursive instances. This is necessary to access available attributes without
* making all of the set's geometry real.
*
* \param limit: The total number of geometry sets to visit before returning early. This is used
* to avoid looking through too many geometry sets recursively, as an explicit tradeoff in favor
* of performance at the cost of visiting every unique attribute.
*/
void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
const AttributeForeachCallback callback,
const int limit)
{
int count = 0;
instances_attribute_foreach_recursive(geometry_set, callback, limit, count);
}
void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
Span<GeometryComponentType> component_types,
const Set<std::string> &ignored_attributes,
Map<std::string, AttributeKind> &r_attributes)
{
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
@ -189,7 +305,7 @@ void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
{attribute_kind->data_type, meta_data.data_type});
};
attributes.add_or_modify(name, add_info, modify_info);
r_attributes.add_or_modify(name, add_info, modify_info);
return true;
});
}
@ -383,10 +499,11 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
/* Don't copy attributes that are stored directly in the mesh data structs. */
Map<std::string, AttributeKind> attributes;
gather_attribute_info(attributes,
component_types,
set_groups,
{"position", "material_index", "normal", "shade_smooth", "crease"});
geometry_set_gather_instances_attribute_info(
set_groups,
component_types,
{"position", "material_index", "normal", "shade_smooth", "crease"},
attributes);
join_attributes(
set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component));
}
@ -410,7 +527,8 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou
PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint);
dst_component.replace(pointcloud);
Map<std::string, AttributeKind> attributes;
gather_attribute_info(attributes, {GEO_COMPONENT_TYPE_POINT_CLOUD}, set_groups, {});
geometry_set_gather_instances_attribute_info(
set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {}, attributes);
join_attributes(set_groups,
{GEO_COMPONENT_TYPE_POINT_CLOUD},
attributes,

View File

@ -222,7 +222,7 @@ void BKE_gpencil_blend_read_data(BlendDataReader *reader, bGPdata *gpd)
/* relink palettes (old palettes deprecated, only to convert old files) */
BLO_read_list(reader, &gpd->palettes);
if (gpd->palettes.first != NULL) {
LISTBASE_FOREACH (Palette *, palette, &gpd->palettes) {
LISTBASE_FOREACH (bGPDpalette *, palette, &gpd->palettes) {
BLO_read_list(reader, &palette->colors);
}
}
@ -1293,7 +1293,8 @@ bGPDframe *BKE_gpencil_layer_frame_find(bGPDlayer *gpl, int cframe)
return NULL;
}
/** Get the appropriate gp-frame from a given layer
/**
* Get the appropriate gp-frame from a given layer
* - this sets the layer's actframe var (if allowed to)
* - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
*

View File

@ -616,7 +616,8 @@ static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob
return remap_cfra;
}
/** Get the current frame re-timed with time modifiers.
/**
* Get the current frame re-timed with time modifiers.
* \param depsgraph: Current depsgraph.
* \param scene: Current scene
* \param ob: Grease pencil object
@ -746,7 +747,8 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
BKE_gpencil_update_orig_pointers(ob_orig, ob);
}
/** Calculate gpencil modifiers.
/**
* Calculate gpencil modifiers.
* \param depsgraph: Current depsgraph
* \param scene: Current scene
* \param ob: Grease pencil object

View File

@ -1264,9 +1264,7 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag);
}
/* We may need our own flag to control that at some point, but for now 'no main' one should be
* good enough. */
if ((orig_flag & LIB_ID_CREATE_NO_MAIN) == 0) {
if ((orig_flag & LIB_ID_COPY_NO_LIB_OVERRIDE) == 0) {
if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
/* We do not want to copy existing override rules here, as they would break the proper
* remapping between IDs. Proper overrides rules will be re-generated anyway. */

View File

@ -196,7 +196,10 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo
static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
{
ID *local_id = BKE_id_copy(bmain, reference_id);
/* Note: We do not want to copy possible override data from reference here (whether it is an
* override template, or already an override of some other ref data). */
ID *local_id = BKE_id_copy_ex(
bmain, reference_id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE);
if (local_id == NULL) {
return NULL;
@ -218,10 +221,12 @@ static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
return local_id;
}
/** Check if given ID has some override rules that actually indicate the user edited it.
/**
* Check if given ID has some override rules that actually indicate the user edited it.
*
* TODO: This could be simplified by storing a flag in IDOverrideLibrary during the diffing
* process? */
* TODO: This could be simplified by storing a flag in #IDOverrideLibrary during the diffing
* process?
*/
bool BKE_lib_override_library_is_user_edited(struct ID *id)
{
if (!ID_IS_OVERRIDE_LIBRARY(id)) {
@ -650,18 +655,6 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root)
return BKE_lib_override_library_create_from_tag(bmain);
}
BLI_INLINE bool lib_override_library_create_post_process_object_is_instantiated(
ViewLayer *view_layer, Object *object, const bool is_resync)
{
/* We cannot rely on check for object being actually instantiated in resync case, because often
* the overridden collection is 'excluded' from the current view-layer.
*
* Fallback to a basic user-count check then, this is weak (since it could lead to some object
* not being instantiated at all), but it should work fine in most common cases. */
return ((is_resync && ID_REAL_USERS(object) >= 1) ||
(!is_resync && BKE_view_layer_base_find(view_layer, object) != NULL));
}
static void lib_override_library_create_post_process(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
@ -672,13 +665,23 @@ static void lib_override_library_create_post_process(Main *bmain,
{
BKE_main_collection_sync(bmain);
if (id_root->newid != NULL) {
/* We create a set of all objects referenced into the scene by its hierarchy of collections.
* NOTE: This is different that the list of bases, since objects in excluded collections etc.
* won't have a base, but are still considered as instanced from our point of view. */
GSet *all_objects_in_scene = BKE_scene_objects_as_gset(scene, NULL);
/* Instantiating the root collection or object should never be needed in resync case, since the
* old override would be remapped to the new one. */
if (!is_resync && id_root != NULL && id_root->newid != NULL) {
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
(Object *)id_reference :
NULL;
Collection *collection_new = ((Collection *)id_root->newid);
if (is_resync && BKE_collection_is_in_scene(collection_new)) {
break;
}
if (ob_reference != NULL) {
BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
}
@ -692,43 +695,16 @@ static void lib_override_library_create_post_process(Main *bmain,
bmain, scene, ((Collection *)id_root), collection_new);
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
if (ob_new != NULL && ob_new->id.override_library != NULL) {
if (ob_reference != NULL) {
Base *base = BKE_view_layer_base_find(view_layer, ob_new);
if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new);
base = BKE_view_layer_base_find(view_layer, ob_new);
DEG_id_tag_update_ex(
bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
BLI_assert(BKE_collection_is_in_scene(collection_new));
if (ob_new == (Object *)ob_reference->id.newid && base != NULL) {
/* TODO: is setting active needed? */
BKE_view_layer_base_select_and_set_active(view_layer, base);
}
}
else if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
BKE_collection_object_add(bmain, collection_new, ob_new);
DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
}
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
break;
}
case ID_OB: {
Object *ob_new = (Object *)id_root->newid;
if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
if (is_resync && residual_storage != NULL) {
BKE_collection_object_add(bmain, residual_storage, ob_new);
}
else {
BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
}
if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
}
break;
}
@ -738,16 +714,15 @@ static void lib_override_library_create_post_process(Main *bmain,
}
/* We need to ensure all new overrides of objects are properly instantiated. */
Collection *default_instantiating_collection = residual_storage;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
Object *ob_new = (Object *)ob->id.newid;
if (ob_new != NULL) {
BLI_assert(ob_new->id.override_library != NULL &&
ob_new->id.override_library->reference == &ob->id);
Collection *default_instantiating_collection = residual_storage;
if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
if (default_instantiating_collection == NULL) {
if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
if (id_root != NULL && default_instantiating_collection == NULL) {
switch (GS(id_root->name)) {
case ID_GR: {
default_instantiating_collection = BKE_collection_add(
@ -768,21 +743,23 @@ static void lib_override_library_create_post_process(Main *bmain,
default_instantiating_collection = collection;
}
}
if (default_instantiating_collection == NULL) {
default_instantiating_collection = scene->master_collection;
}
break;
}
default:
BLI_assert(0);
}
}
if (default_instantiating_collection == NULL) {
default_instantiating_collection = scene->master_collection;
}
BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
}
}
BLI_gset_free(all_objects_in_scene, NULL);
}
/**
@ -893,7 +870,8 @@ bool BKE_lib_override_library_resync(Main *bmain,
ViewLayer *view_layer,
ID *id_root,
Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce)
const bool do_hierarchy_enforce,
const bool do_post_process)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(!ID_IS_LINKED(id_root));
@ -917,7 +895,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
if (id->tag & LIB_TAG_DOIT && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
/* While this should not happen in typical cases (and won't be properly supported here), user
* is free to do all kind of very bad things, including having different local overrides of a
* same linked ID in a same hierarchy. */
@ -1087,6 +1065,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
/* Otherwise, keep them, user needs to decide whether what to do with them. */
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
id_fake_user_set(id);
id->flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name);
}
}
@ -1099,18 +1078,20 @@ bool BKE_lib_override_library_resync(Main *bmain,
*/
id_root = id_root_reference->newid;
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
/* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
* since we already relinked old root override collection to new resync'ed one above. So this
* call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
* that we do not have any stray objects. */
lib_override_library_create_post_process(bmain,
scene,
view_layer,
id_root_reference,
id_root,
override_resync_residual_storage,
true);
if (do_post_process) {
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
/* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
* since we already relinked old root override collection to new resync'ed one above. So this
* call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
* that we do not have any stray objects. */
lib_override_library_create_post_process(bmain,
scene,
view_layer,
id_root_reference,
id_root,
override_resync_residual_storage,
true);
}
/* Cleanup. */
BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
@ -1242,9 +1223,10 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
continue;
}
do_continue = true;
CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
const bool success = BKE_lib_override_library_resync(
bmain, scene, view_layer, id, override_resync_residual_storage, false);
bmain, scene, view_layer, id, override_resync_residual_storage, false, false);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
break;
}
@ -1256,6 +1238,10 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
FOREACH_MAIN_LISTBASE_END;
}
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
lib_override_library_create_post_process(
bmain, scene, view_layer, NULL, NULL, override_resync_residual_storage, true);
if (BKE_collection_is_empty(override_resync_residual_storage)) {
BKE_collection_delete(bmain, override_resync_residual_storage, true);
}
@ -2426,8 +2412,10 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain,
return storage_id;
}
/** Restore given ID modified by \a BKE_lib_override_library_operations_store_start, to its
* original state. */
/**
* Restore given ID modified by #BKE_lib_override_library_operations_store_start, to its
* original state.
*/
void BKE_lib_override_library_operations_store_end(
OverrideLibraryStorage *UNUSED(override_storage), ID *local)
{

View File

@ -231,7 +231,8 @@ const MEdge *MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
return medge;
}
/** Convert all of the meshes in `meshes` to an `IMesh` and return that.
/**
* Convert all of the meshes in `meshes` to an `IMesh` and return that.
* All of the coordinates are transformed into the local space of the
* first Mesh. To do this transformation, we also need the transformation
* obmats corresponding to the Meshes, so they are in the `obmats` argument.
@ -638,7 +639,8 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
}
}
/** Make sure that there are custom data layers in the target mesh
/**
* Make sure that there are custom data layers in the target mesh
* corresponding to all target layers in all of the operands after the first.
* (The target should already have layers for those in the first operand mesh).
* Edges done separately -- will have to be done later, after edges are made.
@ -673,7 +675,8 @@ static void merge_edge_customdata_layers(Mesh *target, MeshesToIMeshInfo &mim)
}
}
/** Convert the output IMesh im to a Blender Mesh,
/**
* Convert the output IMesh im to a Blender Mesh,
* using the information in mim to get all the attributes right.
*/
static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)

View File

@ -172,7 +172,7 @@ class FairingContext {
}
/* Early return, nothing to do. */
if (num_affected_vertices == 0 || num_affected_vertices == totvert_) {
if (ELEM(num_affected_vertices, 0, totvert_)) {
return;
}

View File

@ -434,8 +434,10 @@ NlaStrip *BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker)
return strip;
}
/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
* `IDTypeInfo` structure). */
/**
* Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
* `IDTypeInfo` structure).
*/
void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data)
{
BKE_LIB_FOREACHID_PROCESS(data, strip->act, IDWALK_CB_USER);
@ -1380,8 +1382,10 @@ static void nlastrip_fix_resize_overlaps(NlaStrip *strip)
}
}
/** Recalculate the start and end frames for the strip to match the bounds of its action such that
* the overall NLA animation result is unchanged. */
/**
* Recalculate the start and end frames for the strip to match the bounds of its action such that
* the overall NLA animation result is unchanged.
*/
void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip)
{
float prev_actstart;

View File

@ -1314,8 +1314,8 @@ void nodeUnregisterType(bNodeType *nt)
bool nodeTypeUndefined(bNode *node)
{
return (node->typeinfo == &NodeTypeUndefined) ||
(node->type == NODE_GROUP && node->id && ID_IS_LINKED(node->id) &&
(node->id->tag & LIB_TAG_MISSING));
((node->type == NODE_GROUP || node->type == NODE_CUSTOM_GROUP) && node->id &&
ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING));
}
GHashIterator *nodeTypeGetIterator(void)
@ -3096,10 +3096,12 @@ void ntreeSetOutput(bNodeTree *ntree)
* might be different for editor or for "real" use... */
}
/** Get address of potential nodetree pointer of given ID.
/**
* Get address of potential node-tree pointer of given ID.
*
* \warning Using this function directly is potentially dangerous, if you don't know or are not
* sure, please use `ntreeFromID()` instead. */
* sure, please use `ntreeFromID()` instead.
*/
bNodeTree **BKE_ntree_ptr_from_id(ID *id)
{
switch (GS(id->name)) {
@ -4937,6 +4939,7 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_vector_math();
register_node_type_geo_attribute_remove();
register_node_type_geo_boolean();
register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
register_node_type_geo_edge_split();
register_node_type_geo_is_viewport();

View File

@ -311,8 +311,8 @@ static void object_free_data(ID *id)
/* Free runtime curves data. */
if (ob->runtime.curve_cache) {
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
if (ob->runtime.curve_cache->path) {
free_path(ob->runtime.curve_cache->path);
if (ob->runtime.curve_cache->anim_path_accum_length) {
MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
MEM_freeN(ob->runtime.curve_cache);
ob->runtime.curve_cache = NULL;
@ -1188,8 +1188,8 @@ void BKE_object_free_curve_cache(Object *ob)
if (ob->runtime.curve_cache) {
BKE_displist_free(&ob->runtime.curve_cache->disp);
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
if (ob->runtime.curve_cache->path) {
free_path(ob->runtime.curve_cache->path);
if (ob->runtime.curve_cache->anim_path_accum_length) {
MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
BKE_nurbList_free(&ob->runtime.curve_cache->deformed_nurbs);
MEM_freeN(ob->runtime.curve_cache);
@ -1359,8 +1359,9 @@ static bool object_modifier_type_copy_check(ModifierType md_type)
return !ELEM(md_type, eModifierType_Hook, eModifierType_Collision);
}
/** Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings
* ID), or add one, and return valid `psys` from `ob_dst`.
/**
* Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings ID),
* or add one, and return valid `psys` from `ob_dst`.
*
* \note Order handling is fairly weak here. This code assumes that it is called **before** the
* modifier using the psys is actually copied, and that this copied modifier will be added at the
@ -1392,7 +1393,8 @@ static ParticleSystem *object_copy_modifier_particle_system_ensure(Main *bmain,
return psys_dst;
}
/** Copy a single modifier.
/**
* Copy a single modifier.
*
* \note **Do not** use this function to copy a whole modifier stack (see note below too). Use
* `BKE_object_modifier_stack_copy` instead.
@ -1400,7 +1402,8 @@ static ParticleSystem *object_copy_modifier_particle_system_ensure(Main *bmain,
* \note Complex modifiers relaying on other data (like e.g. dynamic paint or fluid using particle
* systems) are not always 100% 'correctly' copied here, since we have to use heuristics to decide
* which particle system to use or add in `ob_dst`, and it's placement in the stack, etc. If used
* more than once, this function should preferably be called in stack order. */
* more than once, this function should preferably be called in stack order.
*/
bool BKE_object_copy_modifier(
Main *bmain, Scene *scene, Object *ob_dst, const Object *ob_src, ModifierData *md_src)
{
@ -1500,10 +1503,12 @@ bool BKE_object_copy_modifier(
return true;
}
/** Copy a single GPencil modifier.
/**
* Copy a single GPencil modifier.
*
* \note **Do not** use this function to copy a whole modifier stack. Use
* `BKE_object_modifier_stack_copy` instead. */
* `BKE_object_modifier_stack_copy` instead.
*/
bool BKE_object_copy_gpencil_modifier(struct Object *ob_dst, GpencilModifierData *gmd_src)
{
BLI_assert(ob_dst->type == OB_GPENCIL);
@ -1756,6 +1761,10 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_geometry_set_free(ob->runtime.geometry_set_eval);
ob->runtime.geometry_set_eval = NULL;
}
if (ob->runtime.geometry_set_preview != NULL) {
BKE_geometry_set_free(ob->runtime.geometry_set_preview);
ob->runtime.geometry_set_preview = NULL;
}
}
void BKE_object_free_caches(Object *object)
@ -1806,6 +1815,18 @@ void BKE_object_free_caches(Object *object)
}
}
/* Can be called from multiple threads. */
void BKE_object_set_preview_geometry_set(Object *ob, struct GeometrySet *geometry_set)
{
static ThreadMutex mutex = BLI_MUTEX_INITIALIZER;
BLI_mutex_lock(&mutex);
if (ob->runtime.geometry_set_preview != NULL) {
BKE_geometry_set_free(ob->runtime.geometry_set_preview);
}
ob->runtime.geometry_set_preview = geometry_set;
BLI_mutex_unlock(&mutex);
}
/**
* Actual check for internal data, not context or flags.
*/
@ -3244,7 +3265,7 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
if (par->runtime.curve_cache == NULL) {
return false;
}
if (par->runtime.curve_cache->path == NULL) {
if (par->runtime.curve_cache->anim_path_accum_length == NULL) {
return false;
}
@ -3260,12 +3281,12 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
else {
ctime = cu->ctime;
}
CLAMP(ctime, 0.0f, 1.0f);
unit_m4(r_mat);
/* vec: 4 items! */
if (where_on_path(par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : NULL, &radius, NULL)) {
if (BKE_where_on_path(
par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : NULL, &radius, NULL)) {
if (cu->flag & CU_FOLLOW) {
quat_apply_track(quat, ob->trackflag, ob->upflag);
normalize_qt(quat);

View File

@ -2417,14 +2417,15 @@ int do_guides(Depsgraph *depsgraph,
cu = (Curve *)eff->ob->data;
if (pd->flag & PFIELD_GUIDE_PATH_ADD) {
if (where_on_path(
if (BKE_where_on_path(
eff->ob, data->strength * guidetime, guidevec, guidedir, NULL, &radius, &weight) ==
0) {
return 0;
}
}
else {
if (where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0) {
if (BKE_where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) ==
0) {
return 0;
}
}

View File

@ -641,7 +641,15 @@ static BMFace *pbvh_bmesh_face_create(
/* ensure we never add existing face */
BLI_assert(!BM_face_exists(v_tri, 3));
BMFace *f = BM_face_create(pbvh->bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP);
BMFace *f;
if (!e_tri) {
f = BM_face_create_verts(pbvh->bm, v_tri, 3, f_example, BM_CREATE_NOP, true);
}
else {
f = BM_face_create(pbvh->bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP);
}
f->head.hflag = f_example->head.hflag;
BLI_table_gset_insert(node->bm_faces, f);
@ -2294,7 +2302,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
* really buy anything. */
BLI_buffer_clear(deleted_faces);
#define MAX_LS 24
#define MAX_LS 64
BMLoop *l;
BMLoop *ls[MAX_LS];
@ -2318,26 +2326,41 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
BM_LOOPS_OF_VERT_ITER_END;
void *blocks[MAX_LS];
float ws[MAX_LS], w = totl > 1 ? 1.0f / (float)(totl - 1) : 1.0f;
float ws[MAX_LS], w = totl > 0 ? 1.0f / (float)(totl) : 1.0f;
for (int i = 0; i < totl - 1; i++) {
blocks[i] = ls[i + 1]->head.data;
for (int i = 0; i < totl; i++) {
blocks[i] = ls[i]->head.data;
ws[i] = w;
}
if (totl > 1) {
CustomData_bmesh_interp(&pbvh->bm->ldata, blocks, ws, NULL, totl - 1, ls[0]->head.data);
//snap customdata
if (totl > 0) {
CustomData_bmesh_interp(&pbvh->bm->ldata, blocks, ws, NULL, totl, ls[0]->head.data);
//*
BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
BMLoop *l2 = l->v != v_del ? l->next : l;
if (l2 == ls[0]) {
continue;
}
CustomData_bmesh_copy_data(
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l->head.data);
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l2->head.data);
}
BM_LOOPS_OF_VERT_ITER_END;
BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) {
BMLoop *l2 = l->v != v_conn ? l->next : l;
if (l2 == ls[0]) {
continue;
}
CustomData_bmesh_copy_data(
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l->head.data);
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l2->head.data);
}
BM_LOOPS_OF_VERT_ITER_END;
//*/
}
BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
@ -2456,8 +2479,6 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
// log vert in bmlog, but don't update original customata layers, we want them to be
// interpolated
BM_log_vert_before_modified(pbvh->bm_log, v_conn, eq_ctx->cd_vert_mask_offset, true);
// void *dummy;
// BKE_pbvh_bmesh_update_origvert(pbvh, v_conn, &dummy, &dummy, &dummy);
mid_v3_v3v3(v_conn->co, v_conn->co, v_del->co);
add_v3_v3(v_conn->no, v_del->no);
@ -3094,7 +3115,7 @@ static void pbvh_bmesh_create_nodes_fast_recursive(
BKE_pbvh_node_mark_rebuild_draw(n);
BKE_pbvh_node_fully_hidden_set(n, !has_visible);
n->flag |= PBVH_UpdateNormals|PBVH_UpdateCurvatureDir;
n->flag |= PBVH_UpdateNormals | PBVH_UpdateCurvatureDir;
}
}
@ -3254,6 +3275,151 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
return modified;
}
__attribute__((optnone)) static bool cleanup_valence_3_4(PBVH *pbvh,
const float center[3],
const float view_normal[3],
float radius,
const bool use_frontface,
const bool use_projected)
{
bool modified = false;
float radius2 = radius * 1.25;
float rsqr = radius2 * radius2;
for (int n = 0; n < pbvh->totnode; n++) {
PBVHNode *node = pbvh->nodes + n;
/* Check leaf nodes marked for topology update */
bool ok = (node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology);
ok = ok && !(node->flag & PBVH_FullyHidden);
if (!ok) {
continue;
}
PBVHVertexIter vi;
GSetIterator gi;
BMVert *v;
TGSET_ITER (v, node->bm_unique_verts) {
if (len_squared_v3v3(v->co, center) >= rsqr) {
continue;
}
const int val = BM_vert_edge_count(v);
if (val < 3 || val > 4) {
continue;
}
BMIter iter;
BMLoop *l;
BMLoop *ls[4];
BMVert *vs[4];
BMEdge *es[4];
l = v->e->l;
if (!l) {
continue;
}
if (l->v != v) {
l = l->next;
}
bool bad = false;
int i = 0;
for (int j = 0; j < val; j++) {
ls[i++] = l->v == v ? l->next : l;
l = l->prev->radial_next;
if (l->v != v) {
l = l->next;
}
if (l->radial_next == l || l->radial_next->radial_next != l) {
bad = true;
break;
}
for (int k = 0; k < j; k++) {
if (ls[k]->v == ls[j]->v) {
if (ls[j]->next->v != v) {
ls[j] = ls[j]->next;
}
else {
bad = true;
break;
}
}
if (ls[k]->f == ls[j]->f) {
bad = true;
break;
}
}
}
if (bad) {
continue;
}
pbvh_bmesh_vert_remove(pbvh, v);
BMFace *f;
BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
pbvh_bmesh_face_remove(pbvh, f);
}
modified = true;
l = v->e->l;
vs[0] = ls[0]->v;
vs[1] = ls[1]->v;
vs[2] = ls[2]->v;
BMFace *f1 = NULL;
if (vs[0] != vs[1] && vs[1] != vs[2] && vs[0] != vs[2]) {
f1 = pbvh_bmesh_face_create(pbvh, n, vs, NULL, l->f);
}
if (val == 4 && vs[0] != vs[2] && vs[2] != vs[3] && vs[0] != vs[3]) {
vs[0] = ls[0]->v;
vs[1] = ls[2]->v;
vs[2] = ls[3]->v;
BMFace *f2 = pbvh_bmesh_face_create(pbvh, n, vs, NULL, v->e->l->f);
SWAP(void *, f2->l_first->prev->head.data, ls[3]->head.data);
CustomData_bmesh_copy_data(
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &f2->l_first->head.data);
CustomData_bmesh_copy_data(
&pbvh->bm->ldata, &pbvh->bm->ldata, ls[2]->head.data, &f2->l_first->next->head.data);
}
if (f1) {
SWAP(void *, f1->l_first->head.data, ls[0]->head.data);
SWAP(void *, f1->l_first->next->head.data, ls[1]->head.data);
SWAP(void *, f1->l_first->prev->head.data, ls[2]->head.data);
}
BM_vert_kill(pbvh->bm, v);
}
TGSET_ITER_END
}
if (modified) {
pbvh->bm->elem_index_dirty |= BM_VERT | BM_FACE | BM_EDGE;
pbvh->bm->elem_table_dirty |= BM_VERT | BM_FACE | BM_EDGE;
}
return modified;
}
/* Collapse short edges, subdivide long edges */
bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
PBVHTopologyUpdateMode mode,
@ -3288,6 +3454,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
if (view_normal) {
BLI_assert(len_squared_v3(view_normal) != 0.0f);
}
#if 1
if (mode & PBVH_Collapse) {
EdgeQueue q;
@ -3335,7 +3502,13 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
BLI_heapsimple_free(q.heap, NULL);
BLI_mempool_destroy(queue_pool);
}
#endif
if (mode & PBVH_Cleanup) {
modified |= cleanup_valence_3_4(
pbvh, center, view_normal, radius, use_frontface, use_projected);
}
if (modified) {
#ifdef PROXY_ADVANCED
@ -3696,7 +3869,7 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh)
j = 0;
for (int i = 0; i < bvh->totnode; i++) {
if (!(bvh->nodes[i].flag & PBVH_Delete)) {
if (bvh->nodes[i].children_offset >= bvh->totnode-1) {
if (bvh->nodes[i].children_offset >= bvh->totnode - 1) {
printf("error %i %i\n", i, bvh->nodes[i].children_offset);
continue;
}

View File

@ -1350,6 +1350,13 @@ static void write_area(BlendWriter *writer, ScrArea *area)
}
else if (sl->spacetype == SPACE_SPREADSHEET) {
BLO_write_struct(writer, SpaceSpreadsheet, sl);
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
BLO_write_struct(writer, SpreadsheetColumn, column);
BLO_write_struct(writer, SpreadsheetColumnID, column->id);
BLO_write_string(writer, column->id->name);
}
}
}
}
@ -1702,6 +1709,12 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
sspreadsheet->runtime = NULL;
BLO_read_list(reader, &sspreadsheet->columns);
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
BLO_read_data_address(reader, &column->id);
BLO_read_data_address(reader, &column->id->name);
}
}
}

View File

@ -523,7 +523,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
return ta;
}
/** Load a text file.
/**
* Load a text file.
*
* \note Text data-blocks have no user by default, only the 'real user' flag.
*/

View File

@ -28,7 +28,10 @@
#include "BLI_compiler_compat.h"
#include "BLI_fileops.h"
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
#include "BLI_ghash.h"
#include "BLI_index_range.hh"
#include "BLI_map.hh"
#include "BLI_math.h"
#include "BLI_path_util.h"
@ -63,6 +66,10 @@ static CLG_LogRef LOG = {"bke.volume"};
#define VOLUME_FRAME_NONE INT_MAX
using blender::float3;
using blender::float4x4;
using blender::IndexRange;
#ifdef WITH_OPENVDB
# include <atomic>
# include <list>
@ -144,14 +151,14 @@ static struct VolumeFileCache {
blender::Map<int, openvdb::GridBase::Ptr> simplified_grids;
/* Has the grid tree been loaded? */
bool is_loaded;
mutable bool is_loaded;
/* Error message if an error occurred while loading. */
std::string error_msg;
/* User counting. */
int num_metadata_users;
int num_tree_users;
/* Mutex for on-demand reading of tree. */
std::mutex mutex;
mutable std::mutex mutex;
};
struct EntryHasher {
@ -171,10 +178,6 @@ static struct VolumeFileCache {
};
/* Cache */
VolumeFileCache()
{
}
~VolumeFileCache()
{
BLI_assert(cache.empty());
@ -293,7 +296,7 @@ struct VolumeGrid {
}
}
void load(const char *volume_name, const char *filepath)
void load(const char *volume_name, const char *filepath) const
{
/* If already loaded or not file-backed, nothing to do. */
if (is_loaded || entry == nullptr) {
@ -335,7 +338,7 @@ struct VolumeGrid {
is_loaded = true;
}
void unload(const char *volume_name)
void unload(const char *volume_name) const
{
/* Not loaded or not file-backed, nothing to do. */
if (!is_loaded || entry == nullptr) {
@ -436,10 +439,14 @@ struct VolumeGrid {
int simplify_level = 0;
/* OpenVDB grid if it's not shared through the file cache. */
openvdb::GridBase::Ptr local_grid;
/* Indicates if the tree has been loaded for this grid. Note that vdb.tree()
/**
* Indicates if the tree has been loaded for this grid. Note that vdb.tree()
* may actually be loaded by another user while this is false. But only after
* calling load() and is_loaded changes to true is it safe to access. */
bool is_loaded;
* calling load() and is_loaded changes to true is it safe to access.
*
* Const write access to this must be protected by `entry->mutex`.
*/
mutable bool is_loaded;
};
/* Volume Grid Vector
@ -472,14 +479,15 @@ struct VolumeGridVector : public std::list<VolumeGrid> {
metadata.reset();
}
/* Mutex for file loading of grids list. Const write access to the fields after this must be
* protected by locking with this mutex. */
mutable std::mutex mutex;
/* Absolute file path that grids have been loaded from. */
char filepath[FILE_MAX];
/* File loading error message. */
std::string error_msg;
/* File Metadata. */
openvdb::MetaMap::Ptr metadata;
/* Mutex for file loading of grids list. */
std::mutex mutex;
};
#endif
@ -766,10 +774,10 @@ bool BKE_volume_is_loaded(const Volume *volume)
#endif
}
bool BKE_volume_load(Volume *volume, Main *bmain)
bool BKE_volume_load(const Volume *volume, const Main *bmain)
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
const VolumeGridVector &const_grids = *volume->runtime.grids;
if (volume->runtime.frame == VOLUME_FRAME_NONE) {
/* Skip loading this frame, outside of sequence range. */
@ -777,15 +785,19 @@ bool BKE_volume_load(Volume *volume, Main *bmain)
}
if (BKE_volume_is_loaded(volume)) {
return grids.error_msg.empty();
return const_grids.error_msg.empty();
}
/* Double-checked lock. */
std::lock_guard<std::mutex> lock(grids.mutex);
std::lock_guard<std::mutex> lock(const_grids.mutex);
if (BKE_volume_is_loaded(volume)) {
return grids.error_msg.empty();
return const_grids.error_msg.empty();
}
/* Guarded by the lock, we can continue to access the grid vector,
* adding error messages or a new grid, etc. */
VolumeGridVector &grids = const_cast<VolumeGridVector &>(const_grids);
/* Get absolute file path at current frame. */
const char *volume_name = volume->id.name + 2;
char filepath[FILE_MAX];
@ -850,7 +862,10 @@ void BKE_volume_unload(Volume *volume)
/* File Save */
bool BKE_volume_save(Volume *volume, Main *bmain, ReportList *reports, const char *filepath)
bool BKE_volume_save(const Volume *volume,
const Main *bmain,
ReportList *reports,
const char *filepath)
{
#ifdef WITH_OPENVDB
if (!BKE_volume_load(volume, bmain)) {
@ -882,6 +897,32 @@ bool BKE_volume_save(Volume *volume, Main *bmain, ReportList *reports, const cha
#endif
}
bool BKE_volume_min_max(const Volume *volume, float3 &r_min, float3 &r_max)
{
bool have_minmax = false;
#ifdef WITH_OPENVDB
/* TODO: if we know the volume is going to be displayed, it may be good to
* load it as part of dependency graph evaluation for better threading. We
* could also share the bounding box computation in the global volume cache. */
if (BKE_volume_load(const_cast<Volume *>(volume), G.main)) {
for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
float3 grid_min;
float3 grid_max;
if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) {
DO_MIN(grid_min, r_min);
DO_MAX(grid_max, r_max);
have_minmax = true;
}
}
}
#else
UNUSED_VARS(volume, r_min, r_max);
#endif
return have_minmax;
}
BoundBox *BKE_volume_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_VOLUME);
@ -891,41 +932,20 @@ BoundBox *BKE_volume_boundbox_get(Object *ob)
}
if (ob->runtime.bb == nullptr) {
Volume *volume = (Volume *)ob->data;
ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "volume boundbox");
float min[3], max[3];
bool have_minmax = false;
INIT_MINMAX(min, max);
/* TODO: if we know the volume is going to be displayed, it may be good to
* load it as part of dependency graph evaluation for better threading. We
* could also share the bounding box computation in the global volume cache. */
if (BKE_volume_load(volume, G.main)) {
const int num_grids = BKE_volume_num_grids(volume);
for (int i = 0; i < num_grids; i++) {
VolumeGrid *grid = BKE_volume_grid_get(volume, i);
float grid_min[3], grid_max[3];
BKE_volume_grid_load(volume, grid);
if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) {
DO_MIN(grid_min, min);
DO_MAX(grid_max, max);
have_minmax = true;
}
}
}
if (!have_minmax) {
min[0] = min[1] = min[2] = -1.0f;
max[0] = max[1] = max[2] = 1.0f;
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), __func__);
}
const Volume *volume = (Volume *)ob->data;
float3 min, max;
INIT_MINMAX(min, max);
if (!BKE_volume_min_max(volume, min, max)) {
min = float3(-1);
max = float3(1);
}
BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
return ob->runtime.bb;
}
@ -957,7 +977,7 @@ bool BKE_volume_is_points_only(const Volume *volume)
}
for (int i = 0; i < num_grids; i++) {
VolumeGrid *grid = BKE_volume_grid_get(volume, i);
const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
if (BKE_volume_grid_type(grid) != VOLUME_GRID_POINTS) {
return false;
}
@ -1144,7 +1164,23 @@ const char *BKE_volume_grids_frame_filepath(const Volume *volume)
#endif
}
VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index)
const VolumeGrid *BKE_volume_grid_get_for_read(const Volume *volume, int grid_index)
{
#ifdef WITH_OPENVDB
const VolumeGridVector &grids = *volume->runtime.grids;
for (const VolumeGrid &grid : grids) {
if (grid_index-- == 0) {
return &grid;
}
}
return nullptr;
#else
UNUSED_VARS(volume, grid_index);
return nullptr;
#endif
}
VolumeGrid *BKE_volume_grid_get_for_write(Volume *volume, int grid_index)
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
@ -1160,7 +1196,7 @@ VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index)
#endif
}
VolumeGrid *BKE_volume_grid_active_get(const Volume *volume)
const VolumeGrid *BKE_volume_grid_active_get_for_read(const Volume *volume)
{
const int num_grids = BKE_volume_num_grids(volume);
if (num_grids == 0) {
@ -1168,15 +1204,15 @@ VolumeGrid *BKE_volume_grid_active_get(const Volume *volume)
}
const int index = clamp_i(volume->active_grid, 0, num_grids - 1);
return BKE_volume_grid_get(volume, index);
return BKE_volume_grid_get_for_read(volume, index);
}
/* Tries to find a grid with the given name. Make sure that that the volume has been loaded. */
VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name)
const VolumeGrid *BKE_volume_grid_find_for_read(const Volume *volume, const char *name)
{
int num_grids = BKE_volume_num_grids(volume);
for (int i = 0; i < num_grids; i++) {
VolumeGrid *grid = BKE_volume_grid_get(volume, i);
const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
if (STREQ(BKE_volume_grid_name(grid), name)) {
return grid;
}
@ -1187,7 +1223,7 @@ VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name)
/* Grid Loading */
bool BKE_volume_grid_load(const Volume *volume, VolumeGrid *grid)
bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid)
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
@ -1205,7 +1241,7 @@ bool BKE_volume_grid_load(const Volume *volume, VolumeGrid *grid)
#endif
}
void BKE_volume_grid_unload(const Volume *volume, VolumeGrid *grid)
void BKE_volume_grid_unload(const Volume *volume, const VolumeGrid *grid)
{
#ifdef WITH_OPENVDB
const char *volume_name = volume->id.name + 2;
@ -1335,34 +1371,6 @@ void BKE_volume_grid_transform_matrix(const VolumeGrid *volume_grid, float mat[4
/* Grid Tree and Voxels */
bool BKE_volume_grid_bounds(const VolumeGrid *volume_grid, float min[3], float max[3])
{
#ifdef WITH_OPENVDB
/* TODO: we can get this from grid metadata in some cases? */
const openvdb::GridBase::Ptr grid = volume_grid->grid();
BLI_assert(BKE_volume_grid_is_loaded(volume_grid));
openvdb::CoordBBox coordbbox;
if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) {
INIT_MINMAX(min, max);
return false;
}
openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox);
min[0] = (float)bbox.min().x();
min[1] = (float)bbox.min().y();
min[2] = (float)bbox.min().z();
max[0] = (float)bbox.max().x();
max[1] = (float)bbox.max().y();
max[2] = (float)bbox.max().z();
return true;
#else
UNUSED_VARS(volume_grid);
INIT_MINMAX(min, max);
return false;
#endif
}
/* Volume Editing */
Volume *BKE_volume_new_for_eval(const Volume *volume_src)
@ -1410,7 +1418,7 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
BLI_assert(BKE_volume_grid_find(volume, name) == nullptr);
BLI_assert(BKE_volume_grid_find_for_read(volume, name) == nullptr);
BLI_assert(type != VOLUME_GRID_UNKNOWN);
openvdb::GridBase::Ptr vdb_grid = BKE_volume_grid_type_operation(type, CreateGridOp{});
@ -1472,13 +1480,45 @@ float BKE_volume_simplify_factor(const Depsgraph *depsgraph)
/* OpenVDB Grid Access */
#ifdef WITH_OPENVDB
bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, float3 &r_min, float3 &r_max)
{
/* TODO: we can get this from grid metadata in some cases? */
openvdb::CoordBBox coordbbox;
if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) {
return false;
}
openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox);
r_min = float3((float)bbox.min().x(), (float)bbox.min().y(), (float)bbox.min().z());
r_max = float3((float)bbox.max().x(), (float)bbox.max().y(), (float)bbox.max().z());
return true;
}
/**
* Return a new grid pointer with only the metadata and transform changed.
* This is useful for instances, where there is a separate transform on top of the original
* grid transform that must be applied for some operations that only take a grid argument.
*/
openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid,
const blender::float4x4 &transform)
{
openvdb::math::Transform::Ptr grid_transform = grid->transform().copy();
grid_transform->postMult(openvdb::Mat4d(((float *)transform.values)));
/* Create a transformed grid. The underlying tree is shared. */
return grid->copyGridReplacingTransform(grid_transform);
}
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const VolumeGrid *grid)
{
return grid->grid();
}
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const Volume *volume,
VolumeGrid *grid)
const VolumeGrid *grid)
{
BKE_volume_grid_load(volume, grid);
return grid->grid();

View File

@ -104,7 +104,7 @@ static void create_texture_to_object_matrix(const openvdb::Mat4d &grid_transform
#endif
bool BKE_volume_grid_dense_floats(const Volume *volume,
VolumeGrid *volume_grid,
const VolumeGrid *volume_grid,
DenseFloatVolumeGrid *r_dense_grid)
{
#ifdef WITH_OPENVDB
@ -334,7 +334,7 @@ static void boxes_to_cube_mesh(blender::Span<openvdb::CoordBBox> boxes,
#endif
void BKE_volume_grid_wireframe(const Volume *volume,
VolumeGrid *volume_grid,
const VolumeGrid *volume_grid,
BKE_volume_wireframe_cb cb,
void *cb_userdata)
{
@ -411,7 +411,7 @@ static void grow_triangles(blender::MutableSpan<blender::float3> verts,
#endif /* WITH_OPENVDB */
void BKE_volume_grid_selection_surface(const Volume *volume,
VolumeGrid *volume_grid,
const VolumeGrid *volume_grid,
BKE_volume_selection_surface_cb cb,
void *cb_userdata)
{

View File

@ -34,13 +34,19 @@ typedef struct BLI_Iterator {
typedef void (*IteratorCb)(BLI_Iterator *iter);
typedef void (*IteratorBeginCb)(BLI_Iterator *iter, void *data_in);
#define BLI_ITERATOR_INIT(iter) \
{ \
(iter)->skip = false; \
(iter)->valid = true; \
} \
((void)0)
#define ITER_BEGIN(callback_begin, callback_next, callback_end, _data_in, _type, _instance) \
{ \
_type _instance; \
IteratorCb callback_end_func = callback_end; \
BLI_Iterator iter_macro; \
iter_macro.skip = false; \
iter_macro.valid = true; \
BLI_ITERATOR_INIT(&iter_macro); \
for (callback_begin(&iter_macro, (_data_in)); iter_macro.valid; callback_next(&iter_macro)) { \
if (iter_macro.skip) { \
iter_macro.skip = false; \

View File

@ -72,38 +72,46 @@ class ResourceScope : NonCopyable, NonMovable {
* Pass ownership of the resource to the ResourceScope. It will be destructed and freed when
* the collector is destructed.
*/
template<typename T> void add(std::unique_ptr<T> resource, const char *name)
template<typename T> T *add(std::unique_ptr<T> resource, const char *name)
{
BLI_assert(resource.get() != nullptr);
T *ptr = resource.release();
if (ptr == nullptr) {
return nullptr;
}
this->add(
resource.release(),
ptr,
[](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
delete typed_data;
},
name);
return ptr;
}
/**
* Pass ownership of the resource to the ResourceScope. It will be destructed when the
* collector is destructed.
*/
template<typename T> void add(destruct_ptr<T> resource, const char *name)
template<typename T> T *add(destruct_ptr<T> resource, const char *name)
{
T *ptr = resource.release();
if (ptr == nullptr) {
return nullptr;
}
/* There is no need to keep track of such types. */
if (std::is_trivially_destructible_v<T>) {
resource.release();
return;
return ptr;
}
BLI_assert(resource.get() != nullptr);
this->add(
resource.release(),
ptr,
[](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
typed_data->~T();
},
name);
return ptr;
}
/**

View File

@ -78,7 +78,7 @@ float BLI_dial_angle(Dial *dial, const float current_position[2])
cosval = dot_v2v2(current_direction, dial->initial_direction);
sinval = cross_v2v2(current_direction, dial->initial_direction);
/* clamp to avoid nans in acos */
/* clamp to avoid nans in #acos */
angle = atan2f(sinval, cosval);
/* change of sign, we passed the 180 degree threshold. This means we need to add a turn.

View File

@ -334,9 +334,6 @@ template<typename T> class CDT_state {
T epsilon;
explicit CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon);
~CDT_state()
{
}
};
template<typename T> CDTArrangement<T>::~CDTArrangement()

View File

@ -527,9 +527,7 @@ IMeshArena::IMeshArena()
pimpl_ = std::make_unique<IMeshArenaImpl>();
}
IMeshArena::~IMeshArena()
{
}
IMeshArena::~IMeshArena() = default;
void IMeshArena::reserve(int vert_num_hint, int face_num_hint)
{
@ -753,27 +751,6 @@ struct BoundingBox {
BoundingBox(const float3 &min, const float3 &max) : min(min), max(max)
{
}
BoundingBox(const BoundingBox &other) : min(other.min), max(other.max)
{
}
BoundingBox(BoundingBox &&other) noexcept : min(std::move(other.min)), max(std::move(other.max))
{
}
~BoundingBox() = default;
BoundingBox operator=(const BoundingBox &other)
{
if (this != &other) {
min = other.min;
max = other.max;
}
return *this;
}
BoundingBox operator=(BoundingBox &&other) noexcept
{
min = std::move(other.min);
max = std::move(other.max);
return *this;
}
void combine(const float3 &p)
{
@ -936,28 +913,6 @@ class CoplanarCluster {
{
this->add_tri(t, bb);
}
CoplanarCluster(const CoplanarCluster &other) : tris_(other.tris_), bb_(other.bb_)
{
}
CoplanarCluster(CoplanarCluster &&other) noexcept
: tris_(std::move(other.tris_)), bb_(std::move(other.bb_))
{
}
~CoplanarCluster() = default;
CoplanarCluster &operator=(const CoplanarCluster &other)
{
if (this != &other) {
tris_ = other.tris_;
bb_ = other.bb_;
}
return *this;
}
CoplanarCluster &operator=(CoplanarCluster &&other) noexcept
{
tris_ = std::move(other.tris_);
bb_ = std::move(other.bb_);
return *this;
}
/* Assume that caller knows this will not be a duplicate. */
void add_tri(int t, const BoundingBox &bb)
@ -1073,38 +1028,6 @@ struct ITT_value {
ITT_value(ITT_value_kind k, const mpq3 &p1, const mpq3 &p2) : p1(p1), p2(p2), kind(k)
{
}
ITT_value(const ITT_value &other)
: p1(other.p1), p2(other.p2), t_source(other.t_source), kind(other.kind)
{
}
ITT_value(ITT_value &&other) noexcept
: p1(std::move(other.p1)),
p2(std::move(other.p2)),
t_source(other.t_source),
kind(other.kind)
{
}
~ITT_value()
{
}
ITT_value &operator=(const ITT_value &other)
{
if (this != &other) {
kind = other.kind;
p1 = other.p1;
p2 = other.p2;
t_source = other.t_source;
}
return *this;
}
ITT_value &operator=(ITT_value &&other) noexcept
{
kind = other.kind;
p1 = std::move(other.p1);
p2 = std::move(other.p2);
t_source = other.t_source;
return *this;
}
};
static std::ostream &operator<<(std::ostream &os, const ITT_value &itt);

View File

@ -148,10 +148,6 @@ class TBBTaskGroup : public tbb::task_group {
}
# endif
}
~TBBTaskGroup()
{
}
};
#endif

View File

@ -2034,6 +2034,26 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
FOREACH_NODETREE_END;
}
/* The CU_2D flag has been removed. */
LISTBASE_FOREACH (Curve *, cu, &bmain->curves) {
#define CU_2D (1 << 3)
ListBase *nurbs = BKE_curve_nurbs_get(cu);
bool is_2d = true;
LISTBASE_FOREACH (Nurb *, nu, nurbs) {
if (nu->flag & CU_2D) {
nu->flag &= ~CU_2D;
}
else {
is_2d = false;
}
}
#undef CU_2D
if (!is_2d && CU_IS_2D(cu)) {
cu->flag |= CU_3D;
}
}
}
/**
@ -2081,25 +2101,5 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
/* The CU_2D flag has been removed. */
LISTBASE_FOREACH (Curve *, cu, &bmain->curves) {
#define CU_2D (1 << 3)
ListBase *nurbs = BKE_curve_nurbs_get(cu);
bool is_2d = true;
LISTBASE_FOREACH (Nurb *, nu, nurbs) {
if (nu->flag & CU_2D) {
nu->flag &= ~CU_2D;
}
else {
is_2d = false;
}
}
#undef CU_2D
if (!is_2d && CU_IS_2D(cu)) {
cu->flag |= CU_3D;
}
}
}
}

View File

@ -50,10 +50,6 @@
#include "CLG_log.h"
BlendfileLoadingBaseTest::~BlendfileLoadingBaseTest()
{
}
void BlendfileLoadingBaseTest::SetUpTestCase()
{
testing::Test::SetUpTestCase();

View File

@ -30,8 +30,6 @@ class BlendfileLoadingBaseTest : public testing::Test {
struct Depsgraph *depsgraph = nullptr;
public:
virtual ~BlendfileLoadingBaseTest();
/* Sets up Blender just enough to not crash on loading
* a blendfile and constructing a depsgraph. */
static void SetUpTestCase();

View File

@ -38,7 +38,8 @@ namespace blender::meshintersect {
#ifdef WITH_GMP
/** Make a #blender::meshintersect::Mesh from #BMesh bm.
/**
* Make a #blender::meshintersect::Mesh from #BMesh bm.
* We are given a triangulation of it from the caller via #looptris,
* which are looptris_tot triples of loops that together tessellate
* the faces of bm.

View File

@ -608,4 +608,8 @@ endif()
blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
target_compile_options(bf_compositor PRIVATE "-Wsuggest-override")
endif()
add_dependencies(bf_compositor smaa_areatex_header)

View File

@ -451,6 +451,8 @@ void DebugInfo::graphviz(const ExecutionSystem *system)
BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename);
m_file_index++;
std::cout << "Writing compositor debug to: " << filename << "\n";
FILE *fp = BLI_fopen(filename, "wb");
fputs(str, fp);
fclose(fp);

View File

@ -39,10 +39,6 @@ namespace blender::compositor {
**** NodeGraph ****
*******************/
NodeGraph::NodeGraph()
{
}
NodeGraph::~NodeGraph()
{
while (m_nodes.size()) {

View File

@ -56,7 +56,6 @@ class NodeGraph {
Vector<Link> m_links;
public:
NodeGraph();
~NodeGraph();
const Vector<Node *> &nodes() const

View File

@ -259,17 +259,18 @@ std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation)
{
NodeOperationFlags flags = node_operation.get_flags();
os << "NodeOperation(";
os << "id=" << node_operation.get_id();
if (!node_operation.get_name().empty()) {
os << "name=" << node_operation.get_name() << ",";
os << ",name=" << node_operation.get_name();
}
os << "flags={" << flags << "},";
os << ",flags={" << flags << "}";
if (flags.is_read_buffer_operation) {
const ReadBufferOperation *read_operation = (const ReadBufferOperation *)&node_operation;
const MemoryProxy *proxy = read_operation->getMemoryProxy();
if (proxy) {
const WriteBufferOperation *write_operation = proxy->getWriteBufferOperation();
if (write_operation) {
os << "write=" << (NodeOperation &)*write_operation << ",";
os << ",write=" << (NodeOperation &)*write_operation;
}
}
}

View File

@ -251,6 +251,7 @@ struct NodeOperationFlags {
*/
class NodeOperation {
private:
int m_id;
std::string m_name;
Vector<NodeOperationInput> m_inputs;
Vector<NodeOperationOutput> m_outputs;
@ -307,6 +308,16 @@ class NodeOperation {
return m_name;
}
void set_id(const int id)
{
m_id = id;
}
const int get_id() const
{
return m_id;
}
const NodeOperationFlags get_flags() const
{
return flags;

View File

@ -46,10 +46,6 @@ NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, bNo
m_graph.from_bNodeTree(*context, b_nodetree);
}
NodeOperationBuilder::~NodeOperationBuilder()
{
}
void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
{
/* interface handle for nodes */
@ -124,6 +120,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
void NodeOperationBuilder::addOperation(NodeOperation *operation)
{
operation->set_id(m_operations.size());
m_operations.append(operation);
if (m_current_node) {
operation->set_name(m_current_node->getbNode()->name);
@ -691,4 +688,41 @@ void NodeOperationBuilder::group_operations()
}
}
/** Create a graphviz representation of the NodeOperationBuilder. */
std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder)
{
os << "# Builder start\n";
os << "digraph G {\n";
os << " rankdir=LR;\n";
os << " node [shape=box];\n";
for (const NodeOperation *operation : builder.get_operations()) {
os << " op" << operation->get_id() << " [label=\"" << *operation << "\"];\n";
}
os << "\n";
for (const NodeOperationBuilder::Link &link : builder.get_links()) {
os << " op" << link.from()->getOperation().get_id() << " -> op"
<< link.to()->getOperation().get_id() << ";\n";
}
for (const NodeOperation *operation : builder.get_operations()) {
if (operation->get_flags().is_read_buffer_operation) {
const ReadBufferOperation &read_operation = static_cast<const ReadBufferOperation &>(
*operation);
const WriteBufferOperation &write_operation =
*read_operation.getMemoryProxy()->getWriteBufferOperation();
os << " op" << write_operation.get_id() << " -> op" << read_operation.get_id() << ";\n";
}
}
os << "}\n";
os << "# Builder end\n";
return os;
}
std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link)
{
os << link.from()->getOperation().get_id() << " -> " << link.to()->getOperation().get_id();
return os;
}
} // namespace blender::compositor

View File

@ -87,7 +87,6 @@ class NodeOperationBuilder {
public:
NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree);
~NodeOperationBuilder();
const CompositorContext &context() const
{
@ -119,6 +118,16 @@ class NodeOperationBuilder {
return m_active_viewer;
}
const Vector<NodeOperation *> &get_operations() const
{
return m_operations;
}
const Vector<Link> &get_links() const
{
return m_links;
}
protected:
/** Add datatype conversion where needed */
void add_datatype_conversions();
@ -160,4 +169,7 @@ class NodeOperationBuilder {
#endif
};
std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder);
std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link);
} // namespace blender::compositor

View File

@ -55,6 +55,6 @@ struct WorkPackage {
#endif
};
std::ostream &operator<<(std::ostream &os, const WorkPackage &WorkPackage);
std::ostream &operator<<(std::ostream &os, const WorkPackage &work_package);
} // namespace blender::compositor

View File

@ -20,11 +20,6 @@
namespace blender::compositor {
AlphaOverKeyOperation::AlphaOverKeyOperation()
{
/* pass */
}
void AlphaOverKeyOperation::executePixelSampled(float output[4],
float x,
float y,

View File

@ -28,11 +28,6 @@ namespace blender::compositor {
*/
class AlphaOverKeyOperation : public MixBaseOperation {
public:
/**
* Default constructor
*/
AlphaOverKeyOperation();
/**
* The inner loop of this operation.
*/

View File

@ -20,11 +20,6 @@
namespace blender::compositor {
AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation()
{
/* pass */
}
void AlphaOverPremultiplyOperation::executePixelSampled(float output[4],
float x,
float y,

View File

@ -28,11 +28,6 @@ namespace blender::compositor {
*/
class AlphaOverPremultiplyOperation : public MixBaseOperation {
public:
/**
* Default constructor
*/
AlphaOverPremultiplyOperation();
/**
* The inner loop of this operation.
*/

View File

@ -24,11 +24,6 @@
namespace blender::compositor {
CalculateStandardDeviationOperation::CalculateStandardDeviationOperation()
{
/* pass */
}
void CalculateStandardDeviationOperation::executePixel(float output[4],
int /*x*/,
int /*y*/,

View File

@ -34,8 +34,6 @@ class CalculateStandardDeviationOperation : public CalculateMeanOperation {
float m_standardDeviation;
public:
CalculateStandardDeviationOperation();
/**
* The inner loop of this operation.
*/

View File

@ -21,11 +21,6 @@
namespace blender::compositor {
ConvolutionEdgeFilterOperation::ConvolutionEdgeFilterOperation()
{
/* pass */
}
void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/)
{
float in1[4], in2[4], res1[4] = {0.0}, res2[4] = {0.0};

View File

@ -24,7 +24,6 @@ namespace blender::compositor {
class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation {
public:
ConvolutionEdgeFilterOperation();
void executePixel(float output[4], int x, int y, void *data) override;
};

View File

@ -21,11 +21,6 @@
namespace blender::compositor {
DistanceYCCMatteOperation::DistanceYCCMatteOperation()
{
/* pass */
}
float DistanceYCCMatteOperation::calculateDistance(float key[4], float image[4])
{
/* only measure the second 2 values */

View File

@ -30,12 +30,6 @@ namespace blender::compositor {
class DistanceYCCMatteOperation : public DistanceRGBMatteOperation {
protected:
float calculateDistance(float key[4], float image[4]) override;
public:
/**
* Default constructor
*/
DistanceYCCMatteOperation();
};
} // namespace blender::compositor

View File

@ -99,11 +99,6 @@ void MixBaseOperation::deinitExecution()
/* ******** Mix Add Operation ******** */
MixAddOperation::MixAddOperation()
{
/* pass */
}
void MixAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputColor1[4];
@ -128,11 +123,6 @@ void MixAddOperation::executePixelSampled(float output[4], float x, float y, Pix
/* ******** Mix Blend Operation ******** */
MixBlendOperation::MixBlendOperation()
{
/* pass */
}
void MixBlendOperation::executePixelSampled(float output[4],
float x,
float y,
@ -162,11 +152,6 @@ void MixBlendOperation::executePixelSampled(float output[4],
/* ******** Mix Burn Operation ******** */
MixColorBurnOperation::MixColorBurnOperation()
{
/* pass */
}
void MixColorBurnOperation::executePixelSampled(float output[4],
float x,
float y,
@ -245,11 +230,6 @@ void MixColorBurnOperation::executePixelSampled(float output[4],
/* ******** Mix Color Operation ******** */
MixColorOperation::MixColorOperation()
{
/* pass */
}
void MixColorOperation::executePixelSampled(float output[4],
float x,
float y,
@ -290,11 +270,6 @@ void MixColorOperation::executePixelSampled(float output[4],
/* ******** Mix Darken Operation ******** */
MixDarkenOperation::MixDarkenOperation()
{
/* pass */
}
void MixDarkenOperation::executePixelSampled(float output[4],
float x,
float y,
@ -323,11 +298,6 @@ void MixDarkenOperation::executePixelSampled(float output[4],
/* ******** Mix Difference Operation ******** */
MixDifferenceOperation::MixDifferenceOperation()
{
/* pass */
}
void MixDifferenceOperation::executePixelSampled(float output[4],
float x,
float y,
@ -356,11 +326,6 @@ void MixDifferenceOperation::executePixelSampled(float output[4],
/* ******** Mix Difference Operation ******** */
MixDivideOperation::MixDivideOperation()
{
/* pass */
}
void MixDivideOperation::executePixelSampled(float output[4],
float x,
float y,
@ -406,11 +371,6 @@ void MixDivideOperation::executePixelSampled(float output[4],
/* ******** Mix Dodge Operation ******** */
MixDodgeOperation::MixDodgeOperation()
{
/* pass */
}
void MixDodgeOperation::executePixelSampled(float output[4],
float x,
float y,
@ -494,11 +454,6 @@ void MixDodgeOperation::executePixelSampled(float output[4],
/* ******** Mix Glare Operation ******** */
MixGlareOperation::MixGlareOperation()
{
/* pass */
}
void MixGlareOperation::executePixelSampled(float output[4],
float x,
float y,
@ -535,11 +490,6 @@ void MixGlareOperation::executePixelSampled(float output[4],
/* ******** Mix Hue Operation ******** */
MixHueOperation::MixHueOperation()
{
/* pass */
}
void MixHueOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputColor1[4];
@ -577,11 +527,6 @@ void MixHueOperation::executePixelSampled(float output[4], float x, float y, Pix
/* ******** Mix Lighten Operation ******** */
MixLightenOperation::MixLightenOperation()
{
/* pass */
}
void MixLightenOperation::executePixelSampled(float output[4],
float x,
float y,
@ -628,11 +573,6 @@ void MixLightenOperation::executePixelSampled(float output[4],
/* ******** Mix Linear Light Operation ******** */
MixLinearLightOperation::MixLinearLightOperation()
{
/* pass */
}
void MixLinearLightOperation::executePixelSampled(float output[4],
float x,
float y,
@ -676,11 +616,6 @@ void MixLinearLightOperation::executePixelSampled(float output[4],
/* ******** Mix Multiply Operation ******** */
MixMultiplyOperation::MixMultiplyOperation()
{
/* pass */
}
void MixMultiplyOperation::executePixelSampled(float output[4],
float x,
float y,
@ -709,11 +644,6 @@ void MixMultiplyOperation::executePixelSampled(float output[4],
/* ******** Mix Overlay Operation ******** */
MixOverlayOperation::MixOverlayOperation()
{
/* pass */
}
void MixOverlayOperation::executePixelSampled(float output[4],
float x,
float y,
@ -759,11 +689,6 @@ void MixOverlayOperation::executePixelSampled(float output[4],
/* ******** Mix Saturation Operation ******** */
MixSaturationOperation::MixSaturationOperation()
{
/* pass */
}
void MixSaturationOperation::executePixelSampled(float output[4],
float x,
float y,
@ -801,11 +726,6 @@ void MixSaturationOperation::executePixelSampled(float output[4],
/* ******** Mix Screen Operation ******** */
MixScreenOperation::MixScreenOperation()
{
/* pass */
}
void MixScreenOperation::executePixelSampled(float output[4],
float x,
float y,
@ -835,11 +755,6 @@ void MixScreenOperation::executePixelSampled(float output[4],
/* ******** Mix Soft Light Operation ******** */
MixSoftLightOperation::MixSoftLightOperation()
{
/* pass */
}
void MixSoftLightOperation::executePixelSampled(float output[4],
float x,
float y,
@ -881,11 +796,6 @@ void MixSoftLightOperation::executePixelSampled(float output[4],
/* ******** Mix Subtract Operation ******** */
MixSubtractOperation::MixSubtractOperation()
{
/* pass */
}
void MixSubtractOperation::executePixelSampled(float output[4],
float x,
float y,
@ -913,11 +823,6 @@ void MixSubtractOperation::executePixelSampled(float output[4],
/* ******** Mix Value Operation ******** */
MixValueOperation::MixValueOperation()
{
/* pass */
}
void MixValueOperation::executePixelSampled(float output[4],
float x,
float y,

View File

@ -85,115 +85,96 @@ class MixBaseOperation : public NodeOperation {
class MixAddOperation : public MixBaseOperation {
public:
MixAddOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixBlendOperation : public MixBaseOperation {
public:
MixBlendOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixColorBurnOperation : public MixBaseOperation {
public:
MixColorBurnOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixColorOperation : public MixBaseOperation {
public:
MixColorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDarkenOperation : public MixBaseOperation {
public:
MixDarkenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDifferenceOperation : public MixBaseOperation {
public:
MixDifferenceOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDivideOperation : public MixBaseOperation {
public:
MixDivideOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDodgeOperation : public MixBaseOperation {
public:
MixDodgeOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixGlareOperation : public MixBaseOperation {
public:
MixGlareOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixHueOperation : public MixBaseOperation {
public:
MixHueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixLightenOperation : public MixBaseOperation {
public:
MixLightenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixLinearLightOperation : public MixBaseOperation {
public:
MixLinearLightOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixMultiplyOperation : public MixBaseOperation {
public:
MixMultiplyOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixOverlayOperation : public MixBaseOperation {
public:
MixOverlayOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSaturationOperation : public MixBaseOperation {
public:
MixSaturationOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixScreenOperation : public MixBaseOperation {
public:
MixScreenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSoftLightOperation : public MixBaseOperation {
public:
MixSoftLightOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSubtractOperation : public MixBaseOperation {
public:
MixSubtractOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixValueOperation : public MixBaseOperation {
public:
MixValueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};

View File

@ -384,7 +384,7 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4],
int bottom = searchYDown(x, y);
int d1 = y - top, d2 = bottom - y;
/* Fetch the top ang bottom crossing edges: */
/* Fetch the top and bottom crossing edges: */
int e1 = 0, e2 = 0;
sample(m_imageReader, x - 1, top, c);
if (c[1] > 0.0) {

View File

@ -77,10 +77,6 @@ DepsgraphBuilder::DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuild
{
}
DepsgraphBuilder::~DepsgraphBuilder()
{
}
bool DepsgraphBuilder::need_pull_base_into_graph(Base *base)
{
/* Simple check: enabled bases are always part of dependency graph. */

View File

@ -37,7 +37,7 @@ class DepsgraphBuilderCache;
class DepsgraphBuilder {
public:
virtual ~DepsgraphBuilder();
virtual ~DepsgraphBuilder() = default;
virtual bool need_pull_base_into_graph(Base *base);

View File

@ -149,10 +149,6 @@ bool AnimatedPropertyStorage::isPropertyAnimated(const PointerRNA *pointer_rna,
/* Builder cache itself. */
DepsgraphBuilderCache::DepsgraphBuilderCache()
{
}
DepsgraphBuilderCache::~DepsgraphBuilderCache()
{
for (AnimatedPropertyStorage *animated_property_storage :

View File

@ -81,7 +81,6 @@ class AnimatedPropertyStorage {
/* Cached data which can be re-used by multiple builders. */
class DepsgraphBuilderCache {
public:
DepsgraphBuilderCache();
~DepsgraphBuilderCache();
/* Makes sure storage for animated properties exists and initialized for the given ID. */

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