WIP: Rewrite asset browser as separate editor #104978

Closed
Julian Eisel wants to merge 68 commits from asset-browser-grid-view into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
257 changed files with 8392 additions and 7384 deletions
Showing only changes of commit 11d0c91ba5 - Show all commits

View File

@ -157,8 +157,9 @@ option(WITH_BLENDER "Build blender (disable to build only the blender player)" O
mark_as_advanced(WITH_BLENDER)
if(APPLE)
# Currently this causes a build error linking, disable.
set(WITH_BLENDER_THUMBNAILER OFF)
# In future, can be used with `quicklookthumbnailing/qlthumbnailreply` to create file
# thumbnails for say Finder. Turn it off for now.
option(WITH_BLENDER_THUMBNAILER "Build \"blender-thumbnailer\" thumbnail extraction utility" OFF)
elseif(WIN32)
option(WITH_BLENDER_THUMBNAILER "Build \"BlendThumb.dll\" helper for Windows explorer integration" ON)
else()

View File

@ -417,7 +417,8 @@ MODULE_GROUPING = {
BLENDER_REVISION = str(bpy.app.build_hash, 'utf_8')
# '2.83.0 Beta' or '2.83.0' or '2.83.1'
BLENDER_VERSION_DOTS = bpy.app.version_string
BLENDER_VERSION_STRING = bpy.app.version_string
BLENDER_VERSION_DOTS = "%d.%d" % (bpy.app.version[0], bpy.app.version[1])
if BLENDER_REVISION != "Unknown":
# SHA1 Git hash
@ -1724,11 +1725,11 @@ def write_sphinx_conf_py(basepath):
fw("import sys, os\n\n")
fw("extensions = ['sphinx.ext.intersphinx']\n\n")
fw("intersphinx_mapping = {'blender_manual': ('https://docs.blender.org/manual/en/dev/', None)}\n\n")
fw("project = 'Blender %s Python API'\n" % BLENDER_VERSION_DOTS)
fw("project = 'Blender %s Python API'\n" % BLENDER_VERSION_STRING)
fw("master_doc = 'index'\n")
fw("copyright = u'Blender Foundation'\n")
fw("version = '%s'\n" % BLENDER_VERSION_HASH)
fw("release = '%s'\n" % BLENDER_VERSION_HASH)
fw("version = '%s'\n" % BLENDER_VERSION_DOTS)
fw("release = '%s'\n" % BLENDER_VERSION_DOTS)
# Quiet file not in table-of-contents warnings.
fw("exclude_patterns = [\n")
@ -1749,6 +1750,7 @@ except ModuleNotFoundError:
fw("if html_theme == 'sphinx_rtd_theme':\n")
fw(" html_theme_options = {\n")
fw(" 'display_version': False,\n")
# fw(" 'analytics_id': '',\n")
# fw(" 'collapse_navigation': True,\n")
fw(" 'sticky_navigation': False,\n")
@ -1765,10 +1767,15 @@ except ModuleNotFoundError:
fw("html_show_search_summary = True\n")
fw("html_split_index = True\n")
fw("html_static_path = ['static']\n")
fw("templates_path = ['templates']\n")
fw("html_context = {'commit': '%s'}\n" % BLENDER_VERSION_HASH)
fw("html_extra_path = ['static/favicon.ico', 'static/blender_logo.svg']\n")
fw("html_favicon = 'static/favicon.ico'\n")
fw("html_logo = 'static/blender_logo.svg'\n")
fw("html_last_updated_fmt = '%m/%d/%Y'\n\n")
fw("if html_theme == 'sphinx_rtd_theme':\n")
fw(" html_css_files = ['css/version_switch.css']\n")
fw(" html_js_files = ['js/version_switch.js']\n")
# needed for latex, pdf gen
fw("latex_elements = {\n")
@ -2125,6 +2132,9 @@ def copy_theme_assets(basepath):
shutil.copytree(os.path.join(SCRIPT_DIR, "static"),
os.path.join(basepath, "static"),
copy_function=shutil.copy)
shutil.copytree(os.path.join(SCRIPT_DIR, "templates"),
os.path.join(basepath, "templates"),
copy_function=shutil.copy)
def rna2sphinx(basepath):

View File

@ -0,0 +1,127 @@
/* Override RTD theme */
.rst-versions {
border-top: 0px;
overflow: visible;
}
.version-btn.vdeact {
cursor: default;
color: dimgray;
}
.version-btn.vdeact::after {
content: "";
}
#versionwrap {
display: flex;
padding-top: 2px;
font-size: 90%;
justify-content: center;
flex-wrap: wrap;
}
.version-btn {
display: inline-block;
background-color: #272525;
width: 140px;
text-align: center;
padding: 3px 10px;
margin: 0px 5px 4px;
vertical-align: middle;
color: #27AE60;
border: solid 1px #444444;
border-radius: 3px;
cursor: pointer;
z-index: 400;
transition: border-color 0.4s;
}
.version-btn::after {
content:"\f0d8";
display: inline;
font: normal normal normal 16px/1 FontAwesome;
color: #8d8c8c;
vertical-align: top;
padding-left: 0.5em;
}
.version-btn-open::after {
color: gray;
}
.version-btn:hover, .version-btn:focus {
border-color: #525252;
}
.version-btn-open {
color: gray;
border: solid 1px gray;
}
.version-btn.wait {
cursor: wait;
}
.version-btn.disabled {
cursor: not-allowed;
color: dimgray;
}
.version-dialog {
display: none;
position: absolute;
bottom: 28px;
width: 140px;
margin: 0 5px;
padding-bottom: 4px;
background-color: #0003;
border-radius: 3px;
box-shadow: 0 0 6px #000C;
z-index: 999;
max-height: calc(100vh - 30px);
overflow-y: auto;
cursor: default;
}
.version-title {
padding: 5px;
color: black;
text-align: center;
font-size: 102%;
background-color: #27ae60;
border-bottom: solid 1.5px #444;
}
.version-list {
margin-bottom: 4px;
text-align: center;
background-color: #000C;
border: solid 1px gray;
border-radius: 0px 0px 3px 3px;
}
.version-list a, .version-list span, .version-list li {
position: relative;
display: block;
font-size: 98%;
line-height: 1.15;
width: 100%;
margin: 0;
padding: 4px 0px;
color: #404040;
}
.version-list li {
background-color: #ede9e9;
color: #404040;
padding: 1px;
}
.version-list li:hover, .version-list li a:focus {
background-color: #b9cfda;
}
.version-list li.selected, .version-list li.selected:hover {
background-color: #8d8c8c;
}
.version-list li.selected span {
cursor: default;
outline-color: red;
}
.version-arrow {
position: absolute;
width: 8px;
height: 8px;
left: 50%;
bottom: 4px;
margin-left: -4px;
transform: rotate(225deg);
background: #ede9e9;
border: 1px solid gray;
border-width: 1px 0 0 1px;
}

View File

@ -0,0 +1,323 @@
(function() { // switch: v1.2
"use strict";
var versionsFileUrl = "https://docs.blender.org/PROD/versions.json"
var all_versions;
var Popover = function() {
function Popover(id)
{
this.isOpen = false;
this.type = (id === "version-popover");
this.$btn = $('#' + id);
this.$dialog = this.$btn.next();
this.$list = this.$dialog.children("ul");
this.sel = null;
this.beforeInit();
}
Popover.prototype = {
beforeInit : function() {
var that = this;
this.$btn.on("click", function(e) {
that.init();
e.preventDefault();
e.stopPropagation();
});
this.$btn.on("keydown", function(e) {
if (that.btnKeyFilter(e)) {
that.init();
e.preventDefault();
e.stopPropagation();
}
});
},
init : function() {
this.$btn.off("click");
this.$btn.off("keydown");
if (all_versions === undefined) {
this.$btn.addClass("wait");
this.loadVL(this);
}
else {
this.afterLoad();
}
},
loadVL : function(that) {
$.getJSON(versionsFileUrl, function(data) {
all_versions = data;
that.afterLoad();
return true;
}).fail(function() {
console.log("Version Switch Error: versions.json could not be loaded.");
that.$btn.addClass("disabled");
return false;
});
},
afterLoad : function() {
var release = DOCUMENTATION_OPTIONS.VERSION;
const m = release.match(/\d\.\d+/g);
if (m) {
release = m[0];
}
this.warnOld(release, all_versions);
var version = this.getNamed(release);
var list = this.buildList(version);
this.$list.children(":first-child").remove();
this.$list.append(list);
var that = this;
this.$list.on("keydown", function(e) {
that.keyMove(e);
});
this.$btn.removeClass("wait");
this.btnOpenHandler();
this.$btn.on("mousedown", function(e) {
that.btnOpenHandler();
e.preventDefault()
});
this.$btn.on("keydown", function(e) {
if (that.btnKeyFilter(e)) {
that.btnOpenHandler();
}
});
},
warnOld : function(release, all_versions) {
// Note this is effectively disabled now, two issues must fixed:
// * versions.js does not contain a current entry, because that leads to
// duplicate version numbers in the menu. These need to be deduplicated.
// * It only shows the warning after opening the menu to switch version
// when versions.js is loaded. This is too late to be useful.
var current = all_versions.current
if (!current)
{
// console.log("Version Switch Error: no 'current' in version.json.");
return;
}
const m = current.match(/\d\.\d+/g);
if (m) {
current = parseFloat(m[0]);
}
if (release < current) {
var currentURL = window.location.pathname.replace(release, current);
var warning = $('<div class="admonition warning"> ' +
'<p class="first admonition-title">Note</p> ' +
'<p class="last"> ' +
'You are not using the most up to date version of the documentation. ' +
'<a href="#"></a> is the newest version.' +
'</p>' +
'</div>');
warning.find('a').attr('href', currentURL).text(current);
var body = $("div.body");
if (!body.length) {
body = $("div.document");
}
body.prepend(warning);
}
},
buildList : function(v) {
var url = new URL(window.location.href);
let pathSplit = [ "", "api", v ];
if (url.pathname.startsWith("/api/")) {
pathSplit.push(url.pathname.split('/').slice(3).join('/'));
}
else {
pathSplit.push(url.pathname.substring(1));
}
if (this.type) {
var dyn = all_versions;
var cur = v;
}
var buf = [];
var that = this;
$.each(dyn, function(ix, title) {
buf.push("<li");
if (ix === cur) {
buf.push(
' class="selected" tabindex="-1" role="presentation"><span tabindex="-1" role="menuitem" aria-current="page">' +
title + '</spanp></li>');
}
else {
pathSplit[2 + that.type] = ix;
var href = new URL(url);
href.pathname = pathSplit.join('/');
buf.push(' tabindex="-1" role="presentation"><a href ="' + href + '" tabindex="-1">' +
title + '</a></li>');
}
});
return buf.join('');
},
getNamed : function(v) {
$.each(all_versions, function(ix, title) {
if (ix === "master" || ix === "latest") {
var m = title.match(/\d\.\d[\w\d\.]*/)[0];
if (parseFloat(m) == v) {
v = ix;
return false;
}
}
});
return v;
},
dialogToggle : function(speed) {
var wasClose = !this.isOpen;
var that = this;
if (!this.isOpen) {
this.$btn.addClass("version-btn-open");
this.$btn.attr("aria-pressed", true);
this.$dialog.attr("aria-hidden", false);
this.$dialog.fadeIn(speed, function() {
that.$btn.parent().on("focusout", function(e) {
that.focusoutHandler();
e.stopImmediatePropagation();
})
that.$btn.parent().on("mouseleave", function(e) {
that.mouseoutHandler();
e.stopImmediatePropagation();
});
});
this.isOpen = true;
}
else {
this.$btn.removeClass("version-btn-open");
this.$btn.attr("aria-pressed", false);
this.$dialog.attr("aria-hidden", true);
this.$btn.parent().off("focusout");
this.$btn.parent().off("mouseleave");
this.$dialog.fadeOut(speed, function() {
if (this.$sel) {
this.$sel.attr("tabindex", -1);
}
that.$btn.attr("tabindex", 0);
if (document.activeElement !== null && document.activeElement !== document &&
document.activeElement !== document.body) {
that.$btn.focus();
}
});
this.isOpen = false;
}
if (wasClose) {
if (this.$sel) {
this.$sel.attr("tabindex", -1);
}
if (document.activeElement !== null && document.activeElement !== document &&
document.activeElement !== document.body) {
var $nw = this.listEnter();
$nw.attr("tabindex", 0);
$nw.focus();
this.$sel = $nw;
}
}
},
btnOpenHandler : function() {
this.dialogToggle(300);
},
focusoutHandler : function() {
var list = this.$list;
var that = this;
setTimeout(function() {
if (list.find(":focus").length === 0) {
that.dialogToggle(200);
}
}, 200);
},
mouseoutHandler : function() {
this.dialogToggle(200);
},
btnKeyFilter : function(e) {
if (e.ctrlKey || e.shiftKey) {
return false;
}
if (e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) ||
e.key === "ArrowDown" || e.key === "ArrowUp") {
return true;
}
return false;
},
keyMove : function(e) {
if (e.ctrlKey || e.shiftKey) {
return true;
}
var p = true;
var $nw = $(e.target);
switch (e.key) {
case "ArrowUp":
$nw = this.listPrev($nw);
break;
case "ArrowDown":
$nw = this.listNext($nw);
break;
case "Home":
$nw = this.listFirst();
break;
case "End":
$nw = this.listLast();
break;
case "Escape":
$nw = this.listExit();
break;
case "ArrowLeft":
$nw = this.listExit();
break;
case "ArrowRight":
$nw = this.listExit();
break;
default:
p = false;
}
if (p) {
$nw.attr("tabindex", 0);
$nw.focus();
if (this.$sel) {
this.$sel.attr("tabindex", -1);
}
this.$sel = $nw;
e.preventDefault();
e.stopPropagation();
}
},
listPrev : function($nw) {
if ($nw.parent().prev().length !== 0) {
return $nw.parent().prev().children(":first-child");
}
else {
return this.listLast();
}
},
listNext : function($nw) {
if ($nw.parent().next().length !== 0) {
return $nw.parent().next().children(":first-child");
}
else {
return this.listFirst();
}
},
listFirst : function() {
return this.$list.children(":first-child").children(":first-child");
},
listLast : function() {
return this.$list.children(":last-child").children(":first-child");
},
listExit : function() {
this.mouseoutHandler();
return this.$btn;
},
listEnter : function() {
return this.$list.children(":first-child").children(":first-child");
}
};
return Popover
}();
$(document).ready(function() {
var lng_popover = new Popover("version-popover");
});
})();

View File

@ -0,0 +1,17 @@
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="document versions">
<ul id="versionwrap" role="presentation">
<li role="presentation">
<span id="version-popover" class="version-btn" tabindex="0" role="button" aria-label="versions selector" aria-haspopup="true" aria-controls="version-vsnlist" aria-disabled="true">
{{ release }}
</span>
<div class="version-dialog" aria-hidden="true">
<div class="version-arrow" aria-hidden="true"></div>
<div class="version-title">Versions</div>
<ul id="version-vsnlist" class="version-list" role="menu" aria-labelledby="version-popover" aria-hidden="true">
<li role="presentation">Loading...</li>
</ul>
</div>
</li>
</ul>
</div>

View File

@ -669,7 +669,7 @@ class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Pa
@classmethod
def poll(cls, context):
return not use_optix(context) or has_multi_device(context)
return not use_optix(context) or use_multi_device(context)
def draw(self, context):
import _cycles

View File

@ -14,6 +14,8 @@
* limitations under the License.
*/
#include <optional>
#include "blender/sync.h"
#include "blender/util.h"
@ -625,14 +627,35 @@ void BlenderSync::sync_particle_hair(
}
#ifdef WITH_HAIR_NODES
static float4 hair_point_as_float4(BL::HairPoint b_point)
static std::optional<BL::FloatAttribute> find_curves_radius_attribute(BL::Hair b_hair)
{
float4 mP = float3_to_float4(get_float3(b_point.co()));
mP.w = b_point.radius();
for (BL::Attribute &b_attribute : b_hair.attributes) {
if (b_attribute.name() != "radius") {
continue;
}
if (b_attribute.domain() != BL::Attribute::domain_POINT) {
continue;
}
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) {
continue;
}
return BL::FloatAttribute{b_attribute};
}
return std::nullopt;
}
static float4 hair_point_as_float4(BL::Hair b_hair,
std::optional<BL::FloatAttribute> b_attr_radius,
const int index)
{
float4 mP = float3_to_float4(get_float3(b_hair.position_data[index].vector()));
mP.w = b_attr_radius ? b_attr_radius->data[index].value() : 0.0f;
return mP;
}
static float4 interpolate_hair_points(BL::Hair b_hair,
std::optional<BL::FloatAttribute> b_attr_radius,
const int first_point_index,
const int num_points,
const float step)
@ -641,8 +664,8 @@ static float4 interpolate_hair_points(BL::Hair b_hair,
const int point_a = clamp((int)curve_t, 0, num_points - 1);
const int point_b = min(point_a + 1, num_points - 1);
const float t = curve_t - (float)point_a;
return lerp(hair_point_as_float4(b_hair.points[first_point_index + point_a]),
hair_point_as_float4(b_hair.points[first_point_index + point_b]),
return lerp(hair_point_as_float4(b_hair, b_attr_radius, first_point_index + point_a),
hair_point_as_float4(b_hair, b_attr_radius, first_point_index + point_b),
t);
}
@ -671,12 +694,14 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
hair->reserve_curves(num_curves, num_keys);
std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_hair);
/* Export curves and points. */
vector<float> points_length;
for (BL::HairCurve &b_curve : b_hair.curves) {
const int first_point_index = b_curve.first_point_index();
const int num_points = b_curve.num_points();
for (int i = 0; i < num_curves; i++) {
const int first_point_index = b_hair.curve_offset_data[i].value();
const int num_points = b_hair.curve_offset_data[i + 1].value() - first_point_index;
float3 prev_co = zero_float3();
float length = 0.0f;
@ -687,10 +712,9 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Position and radius. */
for (int i = 0; i < num_points; i++) {
BL::HairPoint b_point = b_hair.points[first_point_index + i];
const float3 co = get_float3(b_point.co());
const float radius = b_point.radius();
const float3 co = get_float3(b_hair.position_data[first_point_index + i].vector());
const float radius = b_attr_radius ? b_attr_radius->data[first_point_index + i].value() :
0.0f;
hair->add_curve_key(co, radius);
if (attr_intercept) {
@ -715,7 +739,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Random number per curve. */
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(b_curve.index(), 0));
attr_random->add(hash_uint2_to_float(i, 0));
}
/* Curve. */
@ -737,14 +761,17 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
/* Export motion keys. */
const int num_keys = hair->get_curve_keys().size();
const int num_curves = b_hair.curves.length();
float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
bool have_motion = false;
int num_motion_keys = 0;
int curve_index = 0;
for (BL::HairCurve &b_curve : b_hair.curves) {
const int first_point_index = b_curve.first_point_index();
const int num_points = b_curve.num_points();
std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_hair);
for (int i = 0; i < num_curves; i++) {
const int first_point_index = b_hair.curve_offset_data[i].value();
const int num_points = b_hair.curve_offset_data[i + 1].value() - first_point_index;
Hair::Curve curve = hair->get_curve(curve_index);
curve_index++;
@ -755,7 +782,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
int point_index = first_point_index + i;
if (point_index < num_keys) {
mP[num_motion_keys] = hair_point_as_float4(b_hair.points[point_index]);
mP[num_motion_keys] = hair_point_as_float4(b_hair, b_attr_radius, point_index);
num_motion_keys++;
if (!have_motion) {
@ -774,7 +801,8 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
const float step_size = curve.num_keys > 1 ? 1.0f / (curve.num_keys - 1) : 0.0f;
for (int i = 0; i < curve.num_keys; i++) {
const float step = i * step_size;
mP[num_motion_keys] = interpolate_hair_points(b_hair, first_point_index, num_points, step);
mP[num_motion_keys] = interpolate_hair_points(
b_hair, b_attr_radius, first_point_index, num_points, step);
num_motion_keys++;
}
have_motion = true;

View File

@ -113,22 +113,30 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]);
if (type == LIGHT_SPOT) {
ls->Ng = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
float radius = klight->spot.radius;
const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float radius = klight->spot.radius;
const float3 dir = make_float3(
klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
/* disk oriented normal */
const float3 lightN = normalize(P - center);
ls->P = center;
if (radius > 0.0f)
/* sphere light */
ls->P += disk_light_sample(ls->Ng, randu, randv) * radius;
/* disk light */
ls->P += disk_light_sample(lightN, randu, randv) * radius;
const float invarea = klight->spot.invarea;
ls->pdf = invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
/* we set the light normal to the outgoing direction to support texturing */
ls->Ng = -ls->D;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
ls->Ng, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
}
@ -137,32 +145,33 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
ls->u = uv.x;
ls->v = uv.y;
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
}
else if (type == LIGHT_POINT) {
float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
float radius = klight->spot.radius;
/* disk oriented normal */
const float3 lightN = normalize(P - center);
ls->P = center;
float pdf = 1.0;
if (radius > 0.0f) {
ls->Ng = normalize(P - center);
ls->P += disk_light_sample(ls->Ng, randu, randv) * radius;
pdf = klight->spot.invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
}
else {
ls->Ng = normalize(P - center);
ls->P += disk_light_sample(lightN, randu, randv) * radius;
}
ls->pdf = klight->spot.invarea;
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = pdf;
/* we set the light normal to the outgoing direction to support texturing */
ls->Ng = -ls->D;
ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea;
if (!in_volume_segment && ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
}
else {
/* area light */
@ -263,14 +272,16 @@ ccl_device bool lights_intersect(KernelGlobals kg,
if (type == LIGHT_SPOT) {
/* Spot/Disk light. */
const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t);
const float3 ray_P = ray->P - ray->D * mis_ray_t;
const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float3 lightN = make_float3(
klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
const float radius = klight->spot.radius;
if (radius == 0.0f) {
continue;
}
/* disk oriented normal */
const float3 lightN = normalize(ray_P - lightP);
/* One sided. */
if (dot(ray->D, lightN) >= 0.0f) {
continue;
@ -292,9 +303,10 @@ ccl_device bool lights_intersect(KernelGlobals kg,
continue;
}
/* disk oriented normal */
const float3 lightN = normalize(ray_P - lightP);
float3 P;
const float3 lsN = normalize(ray_P - lightP);
if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lsN, radius, &P, &t)) {
if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lightN, radius, &P, &t)) {
continue;
}
}
@ -427,7 +439,12 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
ls->D = ray_D;
if (type == LIGHT_SPOT) {
ls->Ng = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]);
/* the normal of the oriented disk */
const float3 lightN = normalize(ray_P - center);
/* we set the light normal to the outgoing direction to support texturing*/
ls->Ng = -ls->D;
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
@ -435,7 +452,7 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
/* spot light attenuation */
ls->eval_fac *= spot_light_attenuation(
ls->Ng, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D);
if (ls->eval_fac == 0.0f) {
return false;
@ -447,23 +464,32 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
/* compute pdf */
if (ls->t != FLT_MAX)
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t);
else
ls->pdf = 0.f;
}
else if (type == LIGHT_POINT) {
float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
const float3 lighN = normalize(ray_P - center);
/* we set the light normal to the outgoing direction to support texturing*/
ls->Ng = -ls->D;
ls->Ng = normalize(ray_P - center);
float invarea = klight->spot.invarea;
ls->eval_fac = (0.25f * M_1_PI_F) * invarea;
ls->pdf = invarea;
if (ls->eval_fac == 0.0f) {
return false;
}
float2 uv = map_to_sphere(ls->Ng);
ls->u = uv.x;
ls->v = uv.y;
/* compute pdf */
if (ls->t != FLT_MAX)
ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t);
ls->pdf *= lamp_light_pdf(kg, lighN, -ls->D, ls->t);
else
ls->pdf = 0.f;
}
@ -921,4 +947,4 @@ ccl_device_inline bool light_distribution_sample_new_position(KernelGlobals kg,
}
}
CCL_NAMESPACE_END
CCL_NAMESPACE_END

View File

@ -268,6 +268,12 @@ void MEM_use_guarded_allocator(void);
* Allocate new memory for and constructs an object of type #T.
* #MEM_delete should be used to delete the object. Just calling #MEM_freeN is not enough when #T
* is not a trivial type.
*
* Note that when no arguments are passed, C++ will do recursive member-wise value initialization.
* That is because C++ differentiates between creating an object with `T` (default initialization)
* and `T()` (value initialization), whereby this function does the latter. Value initialization
* rules are complex, but for C-style structs, memory will be zero-initialized. So this doesn't
* match a `malloc()`, but a `calloc()` call in this case. See https://stackoverflow.com/a/4982720.
*/
template<typename T, typename... Args>
inline T *MEM_new(const char *allocation_name, Args &&...args)

@ -1 +1 @@
Subproject commit 620b85f16d03a6aadd7cae56969c9c29b06b992d
Subproject commit 050058417452bfba0cc9ae8692173eb02ac1ef3a

@ -1 +1 @@
Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230
Subproject commit faa9fc7f98e19be54a715c24061185b04dff5b6c

@ -1 +1 @@
Subproject commit 7936dde9ece881d531b1a2ee6c45ddb56d30038c
Subproject commit 61e45814503f51963c91c51aaf764612e7c5dc72

View File

@ -677,7 +677,6 @@ url_manual_mapping = (
("bpy.ops.gpencil.stroke_merge_by_distance*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-merge-by-distance"),
("bpy.ops.node.collapse_hide_unused_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-collapse-hide-unused-toggle"),
("bpy.ops.object.anim_transforms_to_deltas*", "scene_layout/object/editing/apply.html#bpy-ops-object-anim-transforms-to-deltas"),
("bpy.ops.object.convert_proxy_to_override*", "files/linked_libraries/library_overrides.html#bpy-ops-object-convert-proxy-to-override"),
("bpy.ops.object.modifier_copy_to_selected*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy-to-selected"),
("bpy.ops.preferences.app_template_install*", "advanced/app_templates.html#bpy-ops-preferences-app-template-install"),
("bpy.types.actionposemarkers.active_index*", "animation/armatures/properties/pose_library.html#bpy-types-actionposemarkers-active-index"),

View File

@ -298,7 +298,7 @@ def xml2rna(
del value_xml_split
tp_name = 'ARRAY'
# print(" %s.%s (%s) --- %s" % (type(value).__name__, attr, tp_name, subvalue_type))
# print(" %s.%s (%s) --- %s" % (type(value).__name__, attr, tp_name, subvalue_type))
try:
setattr(value, attr, value_xml_coerce)
except ValueError:
@ -340,7 +340,6 @@ def xml2rna(
else:
# print(elems)
if len(elems) == 1:
# sub node named by its type
child_xml_real, = elems
@ -376,7 +375,6 @@ def _get_context_val(context, path):
def xml_file_run(context, filepath, rna_map):
import xml.dom.minidom
xml_nodes = xml.dom.minidom.parse(filepath)
@ -391,27 +389,25 @@ def xml_file_run(context, filepath, rna_map):
value = _get_context_val(context, rna_path)
if value is not Ellipsis and value is not None:
print(" loading XML: %r -> %r" % (filepath, rna_path))
# print(" loading XML: %r -> %r" % (filepath, rna_path))
xml2rna(xml_node, root_rna=value)
def xml_file_write(context, filepath, rna_map, *, skip_typemap=None):
with open(filepath, "w", encoding="utf-8") as file:
fw = file.write
fw("<bpy>\n")
file = open(filepath, "w", encoding="utf-8")
fw = file.write
fw("<bpy>\n")
for rna_path, _xml_tag in rna_map:
# xml_tag is ignored, we get this from the rna
value = _get_context_val(context, rna_path)
rna2xml(fw,
for rna_path, _xml_tag in rna_map:
# xml_tag is ignored, we get this from the rna
value = _get_context_val(context, rna_path)
rna2xml(
fw=fw,
root_rna=value,
method='ATTR',
root_ident=" ",
ident_val=" ",
skip_typemap=skip_typemap,
)
)
fw("</bpy>\n")
file.close()
fw("</bpy>\n")

View File

@ -318,7 +318,8 @@ def load():
use_v3d_tab_menu=kc_prefs.use_v3d_tab_menu,
use_v3d_shade_ex_pie=kc_prefs.use_v3d_shade_ex_pie,
use_gizmo_drag=(is_select_left and kc_prefs.gizmo_action == 'DRAG'),
use_fallback_tool=(True if is_select_left else (kc_prefs.rmb_action == 'FALLBACK_TOOL')),
use_fallback_tool=True,
use_fallback_tool_rmb=(False if is_select_left else kc_prefs.rmb_action == 'FALLBACK_TOOL'),
use_alt_tool_or_cursor=(
(not use_mouse_emulate_3_button) and
(kc_prefs.use_alt_tool if is_select_left else kc_prefs.use_alt_cursor)

View File

@ -16,6 +16,16 @@
#
# ##### END GPL LICENSE BLOCK #####
# ------------------------------------------------------------------------------
# Developer Notes
#
# - This script should run without Blender (no references to the `bpy` module for example).
# - All configuration must be passed into the `generate_keymaps` function (via `Params`).
# - Supporting some combinations of options is becoming increasingly complex,
# especially `Params.select_mouse` & `Params.use_fallback_tool_rmb`.
# To ensure changes don't unintentionally break other configurations, see:
# `source/tools/utils/blender_keyconfig_export_permutations.py --help`
#
# ------------------------------------------------------------------------------
# Configurable Parameters
@ -48,6 +58,8 @@ class Params:
"use_gizmo_drag",
# Use the fallback tool instead of tweak for RMB select.
"use_fallback_tool",
# Only set for RMB select.
"use_fallback_tool_rmb",
# Use pie menu for tab by default (swap 'Tab/Ctrl-Tab').
"use_v3d_tab_menu",
# Use extended pie menu for shading.
@ -65,15 +77,16 @@ class Params:
"v3d_tilde_action",
# Alt-MMB axis switching 'RELATIVE' or 'ABSOLUTE' axis switching.
"v3d_alt_mmb_drag_action",
# File selector actions on single click.
"use_file_single_click",
# Convenience variables:
# (derived from other settings).
#
# This case needs to be checked often,
# Shorthand for: `(params.use_fallback_tool if params.select_mouse == 'RIGHTMOUSE' else False)`.
"use_fallback_tool_rmb",
# Shorthand for: `('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value)`.
# The fallback tool is activated on the same button as selection.
# Shorthand for: `(True if (select_mouse == 'LEFT') else self.use_fallback_tool_rmb)`
"use_fallback_tool_select_mouse",
# Shorthand for: `('CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value)`.
"select_mouse_value_fallback",
# Shorthand for: `{"type": params.select_tweak, "value": 'ANY'}`.
"select_tweak_event",
@ -103,6 +116,7 @@ class Params:
use_select_all_toggle=False,
use_gizmo_drag=True,
use_fallback_tool=False,
use_fallback_tool_rmb=False,
use_v3d_tab_menu=False,
use_v3d_shade_ex_pie=False,
use_v3d_mmb_pan=False,
@ -146,7 +160,6 @@ class Params:
self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'}
self.cursor_tweak_event = None
self.use_fallback_tool = use_fallback_tool
self.tool_modifier = {}
else:
# Left mouse select uses Click event for selection. This is a little
@ -169,7 +182,6 @@ class Params:
self.cursor_set_event = {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True}
self.cursor_tweak_event = {"type": 'EVT_TWEAK_R', "value": 'ANY', "shift": True}
self.use_fallback_tool = True
# Use the "tool" functionality for LMB select.
if use_alt_tool_or_cursor:
@ -197,8 +209,11 @@ class Params:
self.use_file_single_click = use_file_single_click
self.use_fallback_tool = use_fallback_tool
self.use_fallback_tool_rmb = use_fallback_tool_rmb
# Convenience variables:
self.use_fallback_tool_rmb = self.use_fallback_tool if select_mouse == 'RIGHT' else False
self.use_fallback_tool_select_mouse = True if (select_mouse == 'LEFT') else self.use_fallback_tool_rmb
self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value
self.select_tweak_event = {"type": self.select_tweak, "value": 'ANY'}
self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS'
@ -1149,11 +1164,7 @@ def km_uv_editor(params):
items.extend([
# Selection modes.
*_template_items_uv_select_mode(params),
*_template_uv_select(
type=params.select_mouse,
value=('CLICK' if params.use_fallback_tool_rmb else params.select_mouse_value),
legacy=params.legacy,
),
*_template_uv_select(type=params.select_mouse, value=params.select_mouse_value_fallback, legacy=params.legacy),
("uv.mark_seam", {"type": 'E', "value": 'PRESS', "ctrl": True}, None),
("uv.select_loop",
{"type": params.select_mouse, "value": params.select_mouse_value, "alt": True}, None),
@ -6283,7 +6294,8 @@ def km_image_editor_tool_uv_select_box(params, *, fallback):
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"uv.select_box",
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
**(params.select_tweak_event if fallback else params.tool_tweak_event))),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event))),
]},
)
@ -6295,7 +6307,8 @@ def km_image_editor_tool_uv_select_circle(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"uv.select_circle",
**(params.select_tweak_event if fallback else {"type": params.tool_mouse, "value": 'PRESS'}),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
{"type": params.tool_mouse, "value": 'PRESS'}),
properties=[("wait_for_input", False)])),
# No selection fallback since this operates on press.
]},
@ -6310,7 +6323,8 @@ def km_image_editor_tool_uv_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"uv.select_lasso",
**(params.select_tweak_event if fallback else params.tool_tweak_event))),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event))),
]},
)
@ -6402,7 +6416,8 @@ def km_node_editor_tool_select_box(params, *, fallback):
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"node.select_box",
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
**(params.select_tweak_event if fallback else params.tool_tweak_event),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event),
properties=[("tweak", True)],
)),
]},
@ -6415,7 +6430,9 @@ def km_node_editor_tool_select_lasso(params, *, fallback):
{"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'},
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"node.select_lasso", **(params.select_tweak_event if fallback else params.tool_tweak_event),
"node.select_lasso",
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event),
properties=[("tweak", True)]))
]},
)
@ -6430,7 +6447,7 @@ def km_node_editor_tool_select_circle(params, *, fallback):
"node.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
type=params.select_tweak if fallback else params.tool_mouse,
type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
value='ANY' if fallback else 'PRESS',
properties=[("wait_for_input", False)])),
]},
@ -6484,7 +6501,8 @@ def km_3d_view_tool_select_box(params, *, fallback):
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"view3d.select_box",
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
**(params.select_tweak_event if fallback else params.tool_tweak_event))),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event))),
]},
)
@ -6498,7 +6516,7 @@ def km_3d_view_tool_select_circle(params, *, fallback):
"view3d.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
type=params.select_tweak if fallback else params.tool_mouse,
type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
value='ANY' if fallback else 'PRESS',
properties=[("wait_for_input", False)])),
]},
@ -6512,7 +6530,8 @@ def km_3d_view_tool_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"view3d.select_lasso",
**(params.select_tweak_event if fallback else params.tool_tweak_event))),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event))),
]}
)
@ -7394,7 +7413,8 @@ def km_3d_view_tool_edit_gpencil_select_box(params, *, fallback):
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"gpencil.select_box",
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
**(params.select_tweak_event if fallback else params.tool_tweak_event))),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event))),
]},
)
@ -7408,7 +7428,7 @@ def km_3d_view_tool_edit_gpencil_select_circle(params, *, fallback):
"gpencil.select_circle",
# Why circle select should be used on tweak?
# So that RMB or Shift-RMB is still able to set an element as active.
type=params.select_tweak if fallback else params.tool_mouse,
type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse,
value='ANY' if fallback else 'PRESS',
properties=[("wait_for_input", False)])),
]},
@ -7422,7 +7442,8 @@ def km_3d_view_tool_edit_gpencil_select_lasso(params, *, fallback):
{"items": [
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions(
"gpencil.select_lasso",
**(params.select_tweak_event if fallback else params.tool_tweak_event))),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event))),
]}
)
@ -7573,7 +7594,8 @@ def km_sequencer_editor_tool_generic_select_box(params, *, fallback):
# Don't use `tool_maybe_tweak_event`, see comment for this slot.
*([] if (fallback and not params.use_fallback_tool) else _template_items_tool_select_actions_simple(
"sequencer.select_box",
**(params.select_tweak_event if fallback else params.tool_tweak_event),
**(params.select_tweak_event if (fallback and params.use_fallback_tool_select_mouse) else
params.tool_tweak_event),
properties=[("tweak", params.select_mouse == 'LEFTMOUSE')])),
# RMB select can already set the frame, match the tweak tool.

View File

@ -2961,93 +2961,75 @@ class WM_MT_splash_quick_setup(Menu):
bl_label = "Quick Setup"
def draw(self, context):
wm = context.window_manager
# prefs = context.preferences
layout = self.layout
layout.operator_context = 'EXEC_DEFAULT'
layout.label(text="Quick Setup")
split = layout.split(factor=0.25)
split = layout.split(factor=0.14) # Left margin.
split.label()
split = split.split(factor=2.0 / 3.0)
split = split.split(factor=0.73) # Content width.
col = split.column()
col.use_property_split = True
col.use_property_decorate = False
# Languages.
if bpy.app.build_options.international:
sub = col.split(factor=0.35)
row = sub.row()
row.alignment = 'RIGHT'
row.label(text="Language")
prefs = context.preferences
sub.prop(prefs.view, "language", text="")
col.prop(prefs.view, "language")
col.separator()
col.separator()
# Shortcuts.
wm = context.window_manager
kc = wm.keyconfigs.active
kc_prefs = kc.preferences
sub = col.split(factor=0.35)
row = sub.row()
row.alignment = 'RIGHT'
row.label(text="Shortcuts")
text = bpy.path.display_name(wm.keyconfigs.active.name)
sub = col.column(heading="Shortcuts")
text = bpy.path.display_name(kc.name)
if not text:
text = "Blender"
sub.menu("USERPREF_MT_keyconfigs", text=text)
kc = wm.keyconfigs.active
kc_prefs = kc.preferences
has_select_mouse = hasattr(kc_prefs, "select_mouse")
if has_select_mouse:
sub = col.split(factor=0.35)
row = sub.row()
row.alignment = 'RIGHT'
row.label(text="Select With")
sub.row().prop(kc_prefs, "select_mouse", expand=True)
has_select_mouse = True
col.row().prop(kc_prefs, "select_mouse", text="Select With", expand=True)
has_spacebar_action = hasattr(kc_prefs, "spacebar_action")
if has_spacebar_action:
sub = col.split(factor=0.35)
row = sub.row()
row.alignment = 'RIGHT'
row.label(text="Spacebar")
sub.row().prop(kc_prefs, "spacebar_action", expand=True)
has_select_mouse = True
col.row().prop(kc_prefs, "spacebar_action", text="Spacebar")
col.separator()
sub = col.split(factor=0.35)
row = sub.row()
row.alignment = 'RIGHT'
row.label(text="Theme")
# Themes.
sub = col.column(heading="Theme")
label = bpy.types.USERPREF_MT_interface_theme_presets.bl_label
if label == "Presets":
label = "Blender Dark"
sub.menu("USERPREF_MT_interface_theme_presets", text=label)
# Keep height constant
# Keep height constant.
if not has_select_mouse:
col.label()
if not has_spacebar_action:
col.label()
layout.label()
layout.separator(factor=2.0)
row = layout.row()
# Save settings buttons.
sub = layout.row()
sub = row.row()
old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version()
if bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version:
sub.operator("preferences.copy_prev", text=iface_("Load %d.%d Settings", "Operator") % old_version)
sub.operator("preferences.copy_prev", text="Load %d.%d Settings" % old_version)
sub.operator("wm.save_userpref", text="Save New Settings")
else:
sub.label()
sub.label()
sub.operator("wm.save_userpref", text="Next")
layout.separator()
layout.separator()
layout.separator(factor=2.4)
class WM_MT_splash(Menu):

View File

@ -291,6 +291,9 @@ class NLA_MT_context_menu(Menu):
layout.separator()
props = layout.operator("wm.call_panel", text="Rename...")
props.name = "TOPBAR_PT_name"
props.keep_open = False
layout.operator("nla.duplicate", text="Duplicate").linked = False
layout.operator("nla.duplicate", text="Linked Duplicate").linked = True

View File

@ -481,7 +481,7 @@ class TOPBAR_MT_file_export(Menu):
bl_owner_use_filter = False
def draw(self, _context):
self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj) - New")
self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj)")
if bpy.app.build_options.collada:
self.layout.operator("wm.collada_export",
text="Collada (Default) (.dae)")
@ -832,6 +832,14 @@ class TOPBAR_PT_name(Panel):
row = row_with_icon(layout, 'NODE')
row.prop(item, "label", text="")
found = True
elif space_type == 'NLA_EDITOR':
layout.label(text="NLA Strip Name")
item = next(
(strip for strip in context.selected_nla_strips if strip.active), None)
if item:
row = row_with_icon(layout, 'NLA')
row.prop(item, "name", text="")
found = True
else:
if mode == 'POSE' or (mode == 'WEIGHT_PAINT' and context.pose_object):
layout.label(text="Bone Name")

View File

@ -2316,7 +2316,6 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
context, (
({"property": "use_undo_legacy"}, "T60695"),
({"property": "override_auto_resync"}, "T83811"),
({"property": "proxy_to_override_auto_conversion"}, "T91671"),
({"property": "use_cycles_debug"}, None),
({"property": "use_geometry_nodes_legacy"}, "T91274"),
({"property": "show_asset_debug_info"}, None),

View File

@ -2228,8 +2228,6 @@ class VIEW3D_MT_object_relations(Menu):
layout.operator("object.make_override_library", text="Make Library Override...")
layout.operator("object.convert_proxy_to_override")
layout.operator("object.make_dupli_face")
layout.separator()

View File

@ -547,6 +547,8 @@ compositor_node_categories = [
NodeItem("CompositorNodeCombYUVA"),
NodeItem("CompositorNodeSepYCCA"),
NodeItem("CompositorNodeCombYCCA"),
NodeItem("CompositorNodeSeparateXYZ"),
NodeItem("CompositorNodeCombineXYZ"),
NodeItem("CompositorNodeSwitchView"),
NodeItem("CompositorNodeConvertColorSpace"),
]),

View File

@ -65,6 +65,7 @@ else()
)
add_executable(blender-thumbnailer ${SRC} ${SRC_CMD})
setup_platform_linker_flags(blender-thumbnailer)
target_link_libraries(blender-thumbnailer bf_blenlib)
target_link_libraries(blender-thumbnailer ${PTHREADS_LIBRARIES})
endif()

View File

@ -334,11 +334,6 @@ void makeDerivedMesh(struct Depsgraph *depsgraph,
struct Object *ob,
const struct CustomData_MeshMasks *dataMask);
void DM_calc_loop_tangents(DerivedMesh *dm,
bool calc_active_tangent,
const char (*tangent_names)[MAX_NAME],
int tangent_names_len);
#ifdef __cplusplus
}
#endif

View File

@ -365,7 +365,6 @@ void what_does_obaction(struct Object *ob,
char groupname[],
const struct AnimationEvalContext *anim_eval_context);
/* for proxy */
void BKE_pose_copy_pchan_result(struct bPoseChannel *pchanto,
const struct bPoseChannel *pchanfrom);
/**

View File

@ -619,14 +619,6 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *object);
void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, struct Object *object);
void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, struct Object *object);
void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, struct Object *object);
void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph,
struct Object *object,
int pchan_index);
/* -------------------------------------------------------------------- */
/** \name Deform 3D Coordinates by Armature (armature_deform.c)
* \{ */

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 0
#define BLENDER_FILE_SUBVERSION 1
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -123,11 +123,20 @@ struct Collection *BKE_collection_object_find(struct Main *bmain,
bool BKE_collection_is_empty(const struct Collection *collection);
/**
* Add object to collection
* Add object to given collection, ensuring this collection is 'editable' (i.e. local and not a
* liboverride), and finding a suitable parent one otherwise.
*/
bool BKE_collection_object_add(struct Main *bmain,
struct Collection *collection,
struct Object *ob);
/**
* Same as #BKE_collection_object_add, but unconditionally adds the object to the given collection.
*
* NOTE: required in certain cases, like do-versioning or complex ID management tasks.
*/
bool BKE_collection_object_add_notest(struct Main *bmain,
struct Collection *collection,
struct Object *ob);
/**
* Add \a ob_dst to all scene collections that reference object \a ob_src is in.
* Used for copying objects.

View File

@ -278,18 +278,6 @@ bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph,
void BKE_constraint_panel_expand(struct bConstraint *con);
/* Constraints + Proxies function prototypes */
/**
* Rescue all constraints tagged as being #CONSTRAINT_PROXY_LOCAL
* (i.e. added to bone that's proxy-synced in this file).
*/
void BKE_constraints_proxylocal_extract(struct ListBase *dst, struct ListBase *src);
/**
* Returns if the owner of the constraint is proxy-protected.
*/
bool BKE_constraints_proxylocked_owner(struct Object *ob, struct bPoseChannel *pchan);
/* Constraint Evaluation function prototypes */
/**

View File

@ -253,6 +253,11 @@ bool CustomData_free_layer_active(struct CustomData *data, int type, int totelem
*/
void CustomData_free_layers(struct CustomData *data, int type, int totelem);
/**
* Free all anonymous attributes.
*/
void CustomData_free_layers_anonymous(struct CustomData *data, int totelem);
/**
* Returns true if a layer with the specified type exists.
*/

View File

@ -376,10 +376,6 @@ enum {
/** Clear asset data (in case the ID can actually be made local, in copy case asset data is never
* copied over). */
LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR = 1 << 3,
/* Special type-specific options. */
/** For Objects, do not clear the proxy pointers while making the data-block local. */
LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING = 1 << 16,
};
/**

View File

@ -100,6 +100,9 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
* main. You can add more local IDs to be remapped to use new overriding ones by setting their
* LIB_TAG_DOIT tag.
*
* \param owner_library: the library in which the overrides should be created. Besides versioning
* and resync code path, this should always be NULL (i.e. the local .blend file).
*
* \param reference_library: the library from which the linked data being overridden come from
* (i.e. the library of the linked reference ID).
*
@ -109,6 +112,7 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
* \return \a true on success, \a false otherwise.
*/
bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
struct Library *owner_library,
const struct Library *reference_library,
bool do_no_main);
/**
@ -122,16 +126,24 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
*
* \param view_layer: the active view layer to search instantiated collections in, can be NULL (in
* which case \a scene's master collection children hierarchy is used instead).
*
* \param owner_library: the library in which the overrides should be created. Besides versioning
* and resync code path, this should always be NULL (i.e. the local .blend file).
*
* \param id_root: The root ID to create an override from.
*
* \param id_reference: Some reference ID used to do some post-processing after overrides have been
* created, may be NULL. Typically, the Empty object instantiating the linked collection we
* override, currently.
*
* \param r_id_root_override: if not NULL, the override generated for the given \a id_root.
*
* \return true if override was successfully created.
*/
bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct Library *owner_library,
struct ID *id_root,
struct ID *id_reference,
struct ID **r_id_root_override);

View File

@ -63,7 +63,7 @@ enum {
/**
* That ID is not really used by its owner, it's just an internal hint/helper.
* This addresses Their Highest Ugliness the 'from' pointers: Object->from_proxy and Key->from.
* This marks the 'from' pointers issue, like Key->from.
* How to handle that kind of cases totally depends on what caller code is doing... */
IDWALK_CB_LOOPBACK = (1 << 4),
@ -135,7 +135,6 @@ enum {
/** Do not process ID pointers inside embedded IDs. Needed by depsgraph processing e.g. */
IDWALK_IGNORE_EMBEDDED_ID = (1 << 3),
IDWALK_NO_INDIRECT_PROXY_DATA_USAGE = (1 << 8), /* Ugly special case :(((( */
/** Also process internal ID pointers like `ID.newid` or `ID.orig_id`.
* WARNING: Dangerous, use with caution. */
IDWALK_DO_INTERNAL_RUNTIME_POINTERS = (1 << 9),

View File

@ -68,15 +68,6 @@ enum {
* and can cause crashes very easily!
*/
ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3,
/**
* Do not consider proxy/_group pointers of local objects as indirect usages...
* Our oh-so-beloved proxies again...
* Do not consider data used by local proxy object as indirect usage.
* This is needed e.g. in reload scenario,
* since we have to ensure remapping of Armature data of local proxy
* is also performed. Usual nightmare...
*/
ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4,
/** Do not remap library override pointers. */
ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5,
/** Don't touch the user count (use for low level actions such as swapping pointers). */

View File

@ -466,6 +466,8 @@ void BKE_modifiers_foreach_tex_link(struct Object *ob, TexWalkFunc walk, void *u
struct ModifierData *BKE_modifiers_findby_type(const struct Object *ob, ModifierType type);
struct ModifierData *BKE_modifiers_findby_name(const struct Object *ob, const char *name);
struct ModifierData *BKE_modifiers_findby_session_uuid(const struct Object *ob,
const SessionUUID *session_uuid);
void BKE_modifiers_clear_errors(struct Object *ob);
/**
* used for buttons, to find out if the 'draw deformed in edit-mode option is there.
@ -564,7 +566,8 @@ const char *BKE_modifier_path_relbase_from_global(struct Object *ob);
* For a given modifier data, get corresponding original one.
* If the modifier data is already original, return it as-is.
*/
struct ModifierData *BKE_modifier_get_original(struct ModifierData *md);
struct ModifierData *BKE_modifier_get_original(const struct Object *object,
struct ModifierData *md);
struct ModifierData *BKE_modifier_get_evaluated(struct Depsgraph *depsgraph,
struct Object *object,
struct ModifierData *md);

View File

@ -1291,6 +1291,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_NODE_POSTERIZE 327
#define CMP_NODE_CONVERT_COLOR_SPACE 328
#define CMP_NODE_SCENE_TIME 329
#define CMP_NODE_SEPARATE_XYZ 330
#define CMP_NODE_COMBINE_XYZ 331
/* channel toggles */
#define CMP_CHAN_RGB 1

View File

@ -144,18 +144,6 @@ void BKE_object_link_modifiers(struct Object *ob_dst, const struct Object *ob_sr
void BKE_object_free_modifiers(struct Object *ob, int flag);
void BKE_object_free_shaderfx(struct Object *ob, int flag);
/**
* Proxy rule:
* - `lib_object->proxy_from` == the one we borrow from, set temporally while object_update.
* - `local_object->proxy` == pointer to library object, saved in files and read.
* - `local_object->proxy_group` == pointer to collection dupli-object, saved in files and read.
*/
void BKE_object_make_proxy(struct Main *bmain,
struct Object *ob,
struct Object *target,
struct Object *cob);
void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target);
bool BKE_object_exists_check(struct Main *bmain, const struct Object *obtest);
/**
* Actual check for internal data, not context or flags.
@ -444,7 +432,6 @@ void BKE_object_eval_constraints(struct Depsgraph *depsgraph,
struct Object *ob);
void BKE_object_eval_transform_final(struct Depsgraph *depsgraph, struct Object *ob);
bool BKE_object_eval_proxy_copy(struct Depsgraph *depsgraph, struct Object *object);
void BKE_object_eval_uber_transform(struct Depsgraph *depsgraph, struct Object *ob);
void BKE_object_eval_uber_data(struct Depsgraph *depsgraph,
struct Scene *scene,
@ -486,12 +473,6 @@ void BKE_object_handle_data_update(struct Depsgraph *depsgraph,
*/
void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
/**
* Proxy rule:
* - lib_object->proxy_from == the one we borrow from, only set temporal and cleared here.
* - local_object->proxy == pointer to library object, saved in files and read.
*
* Function below is polluted with proxy exceptions, cleanup will follow!
*
* The main object update call, for object matrix, constraints, keys and #DispList (modifiers)
* requires flags to be set!
*
@ -501,8 +482,7 @@ void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene,
void BKE_object_handle_update_ex(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct RigidBodyWorld *rbw,
bool do_proxy_update);
struct RigidBodyWorld *rbw);
void BKE_object_sculpt_data_create(struct Object *ob);

View File

@ -499,7 +499,6 @@ typedef struct SculptSession {
/* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */
struct MVert *mvert;
const float (*vert_normals)[3];
struct MPoly *mpoly;
struct MLoop *mloop;

View File

@ -182,6 +182,7 @@ set(SRC
intern/lib_id_eval.c
intern/lib_id_remapper.cc
intern/lib_override.c
intern/lib_override_proxy_conversion.c
intern/lib_query.c
intern/lib_remap.c
intern/library.c

View File

@ -1998,32 +1998,6 @@ void mesh_get_mapped_verts_coords(Mesh *me_eval, float (*r_cos)[3], const int to
}
}
void DM_calc_loop_tangents(DerivedMesh *dm,
bool calc_active_tangent,
const char (*tangent_names)[MAX_NAME],
int tangent_names_len)
{
BKE_mesh_calc_loop_tangent_ex(
dm->getVertArray(dm),
dm->getPolyArray(dm),
dm->getNumPolys(dm),
dm->getLoopArray(dm),
dm->getLoopTriArray(dm),
dm->getNumLoopTri(dm),
&dm->loopData,
calc_active_tangent,
tangent_names,
tangent_names_len,
(const float(*)[3])CustomData_get_layer(&dm->vertData, CD_NORMAL),
(const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL),
(const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
(const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */
/* result */
&dm->loopData,
dm->getNumLoops(dm),
&dm->tangent_mask);
}
static void mesh_init_origspace(Mesh *mesh)
{
const float default_osf[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};

View File

@ -1956,30 +1956,15 @@ void BKE_pose_blend_read_lib(BlendLibReader *reader, Object *ob, bPose *pose)
return;
}
/* always rebuild to match proxy or lib changes, but on Undo */
/* Always rebuild to match library changes, except on Undo. */
bool rebuild = false;
if (!BLO_read_lib_is_undo(reader)) {
if (ob->proxy || ob->id.lib != arm->id.lib) {
if (ob->id.lib != arm->id.lib) {
rebuild = true;
}
}
if (ob->proxy) {
/* sync proxy layer */
if (pose->proxy_layer) {
arm->layer = pose->proxy_layer;
}
/* sync proxy active bone */
if (pose->proxy_act_bone[0]) {
Bone *bone = BKE_armature_find_bone_name(arm, pose->proxy_act_bone);
if (bone) {
arm->act_bone = bone;
}
}
}
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
BKE_constraint_blend_read_lib(reader, (ID *)ob, &pchan->constraints);

View File

@ -69,8 +69,6 @@
#include "CLG_log.h"
static CLG_LogRef LOG = {"bke.armature"};
/* -------------------------------------------------------------------- */
/** \name Prototypes
* \{ */
@ -2296,161 +2294,6 @@ void BKE_armature_where_is(bArmature *arm)
/** \name Pose Rebuild
* \{ */
/* if bone layer is protected, copy the data from from->pose
* when used with linked libraries this copies from the linked pose into the local pose */
static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
{
bPose *pose = ob->pose, *frompose = from->pose;
bPoseChannel *pchan, *pchanp;
bConstraint *con;
int error = 0;
if (frompose == NULL) {
return;
}
/* in some cases when rigs change, we can't synchronize
* to avoid crashing check for possible errors here */
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
if (pchan->bone->layer & layer_protected) {
if (BKE_pose_channel_find_name(frompose, pchan->name) == NULL) {
CLOG_ERROR(&LOG,
"failed to sync proxy armature because '%s' is missing pose channel '%s'",
from->id.name,
pchan->name);
error = 1;
}
}
}
if (error) {
return;
}
/* clear all transformation values from library */
BKE_pose_rest(frompose, false);
/* copy over all of the proxy's bone groups */
/* TODO: for later
* - implement 'local' bone groups as for constraints
* NOTE: this isn't trivial, as bones reference groups by index not by pointer,
* so syncing things correctly needs careful attention */
BLI_freelistN(&pose->agroups);
BLI_duplicatelist(&pose->agroups, &frompose->agroups);
pose->active_group = frompose->active_group;
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
pchanp = BKE_pose_channel_find_name(frompose, pchan->name);
if (UNLIKELY(pchanp == NULL)) {
/* happens for proxies that become invalid because of a missing link
* for regular cases it shouldn't happen at all */
}
else if (pchan->bone->layer & layer_protected) {
ListBase proxylocal_constraints = {NULL, NULL};
bPoseChannel pchanw;
/* copy posechannel to temp, but restore important pointers */
pchanw = *pchanp;
pchanw.bone = pchan->bone;
pchanw.prev = pchan->prev;
pchanw.next = pchan->next;
pchanw.parent = pchan->parent;
pchanw.child = pchan->child;
pchanw.custom_tx = pchan->custom_tx;
pchanw.bbone_prev = pchan->bbone_prev;
pchanw.bbone_next = pchan->bbone_next;
pchanw.mpath = pchan->mpath;
pchan->mpath = NULL;
/* Reset runtime data, we don't want to share that with the proxy. */
BKE_pose_channel_runtime_reset_on_copy(&pchanw.runtime);
/* this is freed so copy a copy, else undo crashes */
if (pchanw.prop) {
pchanw.prop = IDP_CopyProperty(pchanw.prop);
/* use the values from the existing props */
if (pchan->prop) {
IDP_SyncGroupValues(pchanw.prop, pchan->prop);
}
}
/* Constraints - proxy constraints are flushed... local ones are added after
* 1: extract constraints not from proxy (CONSTRAINT_PROXY_LOCAL) from pchan's constraints.
* 2: copy proxy-pchan's constraints on-to new.
* 3: add extracted local constraints back on top.
*
* Note for BKE_constraints_copy:
* When copying constraints, disable 'do_extern' otherwise
* we get the libs direct linked in this blend.
*/
BKE_constraints_proxylocal_extract(&proxylocal_constraints, &pchan->constraints);
BKE_constraints_copy(&pchanw.constraints, &pchanp->constraints, false);
BLI_movelisttolist(&pchanw.constraints, &proxylocal_constraints);
/* constraints - set target ob pointer to own object */
for (con = pchanw.constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == from) {
ct->tar = ob;
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
}
}
/* free stuff from current channel */
BKE_pose_channel_free(pchan);
/* copy data in temp back over to the cleaned-out (but still allocated) original channel */
*pchan = pchanw;
if (pchan->custom) {
id_us_plus(&pchan->custom->id);
}
}
else {
/* always copy custom shape */
pchan->custom = pchanp->custom;
if (pchan->custom) {
id_us_plus(&pchan->custom->id);
}
if (pchanp->custom_tx) {
pchan->custom_tx = BKE_pose_channel_find_name(pose, pchanp->custom_tx->name);
}
/* ID-Property Syncing */
{
IDProperty *prop_orig = pchan->prop;
if (pchanp->prop) {
pchan->prop = IDP_CopyProperty(pchanp->prop);
if (prop_orig) {
/* copy existing values across when types match */
IDP_SyncGroupValues(pchan->prop, prop_orig);
}
}
else {
pchan->prop = NULL;
}
if (prop_orig) {
IDP_FreeProperty(prop_orig);
}
}
}
}
}
/**
* \param r_last_visited_bone_p: The last bone handled by the last call to this function.
*/
@ -2579,16 +2422,6 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
// printf("rebuild pose %s, %d bones\n", ob->id.name, counter);
/* synchronize protected layers with proxy */
/* HACK! To preserve 2.7x behavior that you always can pose even locked bones,
* do not do any restoration if this is a COW temp copy! */
/* Switched back to just NO_MAIN tag, for some reasons (c)
* using COW tag was working this morning, but not anymore... */
if (ob->proxy != NULL && (ob->id.tag & LIB_TAG_NO_MAIN) == 0) {
BKE_object_copy_proxy_drivers(ob, ob->proxy);
pose_proxy_sync(ob, ob->proxy, arm->layer_protected);
}
BKE_pose_update_constraint_flags(pose); /* for IK detection for example */
pose->flag &= ~POSE_RECALC;

View File

@ -850,10 +850,6 @@ void BKE_pose_eval_init(struct Depsgraph *depsgraph, Scene *UNUSED(scene), Objec
}
BLI_assert(pose->chan_array != NULL || BLI_listbase_is_empty(&pose->chanbase));
if (object->proxy != NULL) {
object->proxy->proxy_from = object;
}
}
void BKE_pose_eval_init_ik(struct Depsgraph *depsgraph, Scene *scene, Object *object)
@ -1070,57 +1066,3 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, Scene *scene, Object *ob
BIK_release_tree(scene, object, ctime);
pose_eval_cleanup_common(object);
}
void BKE_pose_eval_proxy_init(struct Depsgraph *depsgraph, Object *object)
{
BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
BLI_assert(object->pose->chan_array != NULL || BLI_listbase_is_empty(&object->pose->chanbase));
}
void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, Object *object)
{
BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
}
void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, Object *object)
{
BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
pose_eval_cleanup_common(object);
}
void BKE_pose_eval_proxy_copy_bone(struct Depsgraph *depsgraph, Object *object, int pchan_index)
{
const bArmature *armature = (bArmature *)object->data;
if (armature->edbo != NULL) {
return;
}
BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
bPoseChannel *pchan = pose_pchan_get_indexed(object, pchan_index);
BLI_assert(pchan != NULL);
DEG_debug_print_eval_subdata(
depsgraph, __func__, object->id.name, object, "pchan", pchan->name, pchan);
/* TODO(sergey): Use indexed lookup, once it's guaranteed to be kept
* around for the time while proxies are evaluating.
*/
#if 0
bPoseChannel *pchan_from = pose_pchan_get_indexed(object->proxy_from, pchan_index);
#else
bPoseChannel *pchan_from = BKE_pose_channel_find_name(object->proxy_from->pose, pchan->name);
#endif
if (pchan_from == NULL) {
printf(
"WARNING: Could not find bone %s in linked ID anymore... "
"You should delete and re-generate your proxy.\n",
pchan->name);
return;
}
BKE_pose_copy_pchan_result(pchan, pchan_from);
copy_dq_dq(&pchan->runtime.deform_dual_quat, &pchan_from->runtime.deform_dual_quat);
BKE_pchan_bbone_segments_cache_copy(pchan, pchan_from);
pose_channel_flush_to_orig_if_needed(depsgraph, object, pchan);
}

View File

@ -90,10 +90,10 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
}
case ID_HA: {
Hair *hair = (Hair *)id;
info[ATTR_DOMAIN_POINT].customdata = &hair->pdata;
info[ATTR_DOMAIN_POINT].length = hair->totpoint;
info[ATTR_DOMAIN_CURVE].customdata = &hair->cdata;
info[ATTR_DOMAIN_CURVE].length = hair->totcurve;
info[ATTR_DOMAIN_POINT].customdata = &hair->geometry.point_data;
info[ATTR_DOMAIN_POINT].length = hair->geometry.point_size;
info[ATTR_DOMAIN_CURVE].customdata = &hair->geometry.curve_data;
info[ATTR_DOMAIN_CURVE].length = hair->geometry.curve_size;
break;
}
default:

View File

@ -78,6 +78,23 @@
/** \name High Level `.blend` file read/write.
* \{ */
static bool blendfile_or_libraries_versions_atleast(Main *bmain,
const short versionfile,
const short subversionfile)
{
if (!MAIN_VERSION_ATLEAST(bmain, versionfile, subversionfile)) {
return false;
}
LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
if (!MAIN_VERSION_ATLEAST(library, versionfile, subversionfile)) {
return false;
}
}
return true;
}
static bool foreach_path_clean_cb(BPathForeachPathData *UNUSED(bpath_data),
char *path_dst,
const char *path_src)
@ -349,10 +366,11 @@ static void setup_app_data(bContext *C,
do_versions_ipos_to_animato(bmain);
}
/* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */
/* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a
* version bump and check here. */
if (mode != LOAD_UNDO && !USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) {
/* NOTE: readfile's `do_version` does not allow to create new IDs, and only operates on a single
* library at a time. This code needs to operate on the whole Main at once. */
/* NOTE: Check bmain version (i.e. current blend file version), AND the versions of all the
* linked libraries. */
if (mode != LOAD_UNDO && !blendfile_or_libraries_versions_atleast(bmain, 302, 1)) {
BKE_lib_override_library_main_proxy_convert(bmain, reports);
}
@ -603,12 +621,12 @@ UserDef *BKE_blendfile_userdef_from_defaults(void)
const char *addons[] = {
"io_anim_bvh",
"io_curve_svg",
"io_import_obj",
"io_mesh_ply",
"io_mesh_stl",
"io_mesh_uv_layout",
"io_scene_fbx",
"io_scene_gltf2",
"io_scene_obj",
"io_scene_x3d",
"cycles",
"pose_library",

View File

@ -993,6 +993,27 @@ static int foreach_libblock_link_append_callback(LibraryIDLinkCallbackData *cb_d
/** \name Library link/append code.
* \{ */
static void blendfile_link_append_proxies_convert(Main *bmain, ReportList *reports)
{
/* NOTE: Do not bother checking file versions here, if there are no proxies to convert this code
* is quite fast anyway. */
BlendFileReadReport bf_reports = {.reports = reports};
BKE_lib_override_library_main_proxy_convert(bmain, &bf_reports);
if (bf_reports.count.proxies_to_lib_overrides_success != 0 ||
bf_reports.count.proxies_to_lib_overrides_failures != 0) {
BKE_reportf(
bf_reports.reports,
RPT_WARNING,
"Proxies have been removed from Blender (%d proxies were automatically converted "
"to library overrides, %d proxies could not be converted and were cleared). "
"Please consider re-saving any library .blend file with the newest Blender version.",
bf_reports.count.proxies_to_lib_overrides_success,
bf_reports.count.proxies_to_lib_overrides_failures);
}
}
void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *reports)
{
if (lapp_context->num_items == 0) {
@ -1040,10 +1061,6 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *
if (item->action != LINK_APPEND_ACT_UNSET) {
/* Already set, pass. */
}
if (GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) {
CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name);
item->action = LINK_APPEND_ACT_KEEP_LINKED;
}
else if (do_reuse_local_id && existing_local_id != NULL) {
CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name);
item->action = LINK_APPEND_ACT_REUSE_LOCAL;
@ -1098,10 +1115,7 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *
local_appended_new_id = id->newid;
break;
case LINK_APPEND_ACT_MAKE_LOCAL:
BKE_lib_id_make_local(bmain,
id,
make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL |
LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
BKE_lib_id_make_local(bmain, id, make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL);
BLI_assert(id->newid == NULL);
local_appended_new_id = id;
break;
@ -1210,55 +1224,11 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *
continue;
}
BLI_assert(ID_IS_LINKED(id));
/* Attempt to re-link copied proxy objects. This allows appending of an entire scene
* from another blend file into this one, even when that blend file contains proxified
* armatures that have local references. Since the proxified object needs to be linked
* (not local), this will only work when the "Localize all" checkbox is disabled.
* TL;DR: this is a dirty hack on top of an already weak feature (proxies). */
if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) {
Object *ob = (Object *)id;
Object *ob_new = (Object *)id->newid;
bool is_local = false, is_lib = false;
/* Proxies only work when the proxified object is linked-in from a library. */
if (!ID_IS_LINKED(ob->proxy)) {
CLOG_WARN(&LOG,
"Proxy object %s will lose its link to %s, because the "
"proxified object is local",
id->newid->name,
ob->proxy->id.name);
continue;
}
BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib);
/* We can only switch the proxy'ing to a made-local proxy if it is no longer
* referred to from a library. Not checking for local use; if new local proxy
* was not used locally would be a nasty bug! */
if (is_local || is_lib) {
CLOG_WARN(&LOG,
"Made-local proxy object %s will lose its link to %s, "
"because the linked-in proxy is referenced (is_local=%i, is_lib=%i)",
id->newid->name,
ob->proxy->id.name,
is_local,
is_lib);
}
else {
/* we can switch the proxy'ing from the linked-in to the made-local proxy.
* BKE_object_make_proxy() shouldn't be used here, as it allocates memory that
* was already allocated by object_make_local() (which called BKE_object_copy). */
ob_new->proxy = ob->proxy;
ob_new->proxy_group = ob->proxy_group;
ob_new->proxy_from = ob->proxy_from;
ob_new->proxy->proxy_from = ob_new;
ob->proxy = ob->proxy_from = ob->proxy_group = NULL;
}
}
}
BKE_main_id_newptr_and_tag_clear(bmain);
blendfile_link_append_proxies_convert(bmain, reports);
}
void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *reports)
@ -1361,6 +1331,10 @@ void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *re
.active_collection = NULL};
loose_data_instantiate(&instantiate_context);
}
if ((lapp_context->params->flag & FILE_LINK) != 0) {
blendfile_link_append_proxies_convert(lapp_context->params->bmain, reports);
}
}
/** \} */
@ -1541,7 +1515,6 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
/* Note that in reload case, we also want to replace indirect usages. */
const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE |
ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE |
(do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE);
for (item_idx = 0, itemlink = lapp_context->items.list; itemlink;
item_idx++, itemlink = itemlink->next) {

View File

@ -1094,14 +1094,12 @@ static bool collection_object_remove(Main *bmain,
return true;
}
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Object *ob)
{
if (ELEM(NULL, collection, ob)) {
if (ob == NULL) {
return false;
}
collection = collection_parent_editable_find_recursive(collection);
/* Only case where this pointer can be NULL is when scene itself is linked, this case should
* never be reached. */
BLI_assert(collection != NULL);
@ -1122,6 +1120,17 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
return true;
}
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
{
if (collection == NULL) {
return false;
}
collection = collection_parent_editable_find_recursive(collection);
return BKE_collection_object_add_notest(bmain, collection, ob);
}
void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst)
{
bool is_instantiated = false;

View File

@ -5850,14 +5850,6 @@ static void add_new_constraint_to_list(Object *ob, bPoseChannel *pchan, bConstra
BLI_addtail(list, con);
BKE_constraint_unique_name(con, list);
/* if the target list is a list on some PoseChannel belonging to a proxy-protected
* Armature layer, we must tag newly added constraints with a flag which allows them
* to persist after proxy syncing has been done
*/
if (BKE_constraints_proxylocked_owner(ob, pchan)) {
con->flag |= CONSTRAINT_PROXY_LOCAL;
}
/* make this constraint the active one */
BKE_constraints_active_set(list, con);
}
@ -6213,45 +6205,6 @@ bool BKE_constraint_is_nonlocal_in_liboverride(const Object *ob, const bConstrai
(con == NULL || (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) == 0));
}
/* -------- Constraints and Proxies ------- */
void BKE_constraints_proxylocal_extract(ListBase *dst, ListBase *src)
{
bConstraint *con, *next;
/* for each tagged constraint, remove from src and move to dst */
for (con = src->first; con; con = next) {
next = con->next;
/* check if tagged */
if (con->flag & CONSTRAINT_PROXY_LOCAL) {
BLI_remlink(src, con);
BLI_addtail(dst, con);
}
}
}
bool BKE_constraints_proxylocked_owner(Object *ob, bPoseChannel *pchan)
{
/* Currently, constraints can only be on object or bone level */
if (ob && ob->proxy) {
if (ob->pose && pchan) {
bArmature *arm = ob->data;
/* On bone-level, check if bone is on proxy-protected layer */
if ((pchan->bone) && (pchan->bone->layer & arm->layer_protected)) {
return true;
}
}
else {
/* FIXME: constraints on object-level are not handled well yet */
return true;
}
}
return false;
}
/* -------- Target-Matrix Stuff ------- */
void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,

View File

@ -899,7 +899,7 @@ float BKE_nurb_calc_length(const Nurb *nu, int resolution)
pntsit = points + 3;
}
while (--b) {
while (--b > 0) {
length += len_v3v3(prevpntsit, pntsit);
prevpntsit = pntsit;
pntsit += 3;

View File

@ -1793,10 +1793,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(float[3]), "vec3f", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 44: CD_RADIUS */
{sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 45: CD_HAIRCURVE */
{sizeof(HairCurve), "HairCurve", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 46: CD_HAIRMAPPING */
{sizeof(HairMapping), "HairMapping", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 45: CD_HAIRCURVE */ /* UNUSED */
{-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 46: CD_HAIRMAPPING */ /* UNUSED */
{-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 47: CD_PROP_COLOR */
{sizeof(MPropCol),
"MPropCol",
@ -2431,7 +2431,7 @@ const char *CustomData_get_active_layer_name(const struct CustomData *data, cons
{
/* Get the layer index of the active layer of this type. */
const int layer_index = CustomData_get_active_layer_index(data, type);
return layer_index < 0 ? NULL : data->layers[layer_index].name;
return layer_index < 0 ? nullptr : data->layers[layer_index].name;
}
void CustomData_set_layer_active(CustomData *data, int type, int n)
@ -2780,6 +2780,24 @@ void CustomData_free_layers(CustomData *data, int type, int totelem)
}
}
void CustomData_free_layers_anonymous(struct CustomData *data, int totelem)
{
while (true) {
bool found_anonymous_layer = false;
for (int i = 0; i < data->totlayer; i++) {
const CustomDataLayer *layer = &data->layers[i];
if (layer->anonymous_id != nullptr) {
CustomData_free_layer(data, layer->type, totelem, i);
found_anonymous_layer = true;
break;
}
}
if (!found_anonymous_layer) {
break;
}
}
}
bool CustomData_has_layer(const CustomData *data, int type)
{
return (CustomData_get_layer_index(data, type) != -1);

View File

@ -88,14 +88,6 @@ typedef struct DriverVarTypeInfo {
/** \name Driver Target Utilities
* \{ */
static ID *dtar_id_ensure_proxy_from(ID *id)
{
if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) {
return (ID *)(((Object *)id)->proxy_from);
}
return id;
}
/**
* Helper function to obtain a value using RNA from the specified source
* (for evaluating drivers).
@ -113,7 +105,7 @@ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar)
return 0.0f;
}
id = dtar_id_ensure_proxy_from(dtar->id);
id = dtar->id;
/* Error check for missing pointer. */
if (id == NULL) {
@ -217,7 +209,7 @@ bool driver_get_variable_property(ChannelDriver *driver,
return false;
}
id = dtar_id_ensure_proxy_from(dtar->id);
id = dtar->id;
/* Error check for missing pointer. */
if (id == NULL) {
@ -273,7 +265,7 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar)
short valid_targets = 0;
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
Object *ob = (Object *)dtar->id;
/* Check if this target has valid data. */
if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) {
@ -328,7 +320,7 @@ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar)
for (int i = 0; i < 2; i++) {
/* Get pointer to loc values to store in. */
DriverTarget *dtar = &dvar->targets[i];
Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
Object *ob = (Object *)dtar->id;
bPoseChannel *pchan;
/* After the checks above, the targets should be valid here. */
@ -389,7 +381,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
/* NOTE: for now, these are all just world-space */
DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
/* Get pointer to loc values to store in. */
Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
Object *ob = (Object *)dtar->id;
bPoseChannel *pchan;
float tmp_loc[3];
@ -472,7 +464,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
{
DriverTarget *dtar = &dvar->targets[0];
Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id);
Object *ob = (Object *)dtar->id;
bPoseChannel *pchan;
float mat[4][4];
float oldEul[3] = {0.0f, 0.0f, 0.0f};

View File

@ -28,10 +28,11 @@
#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vec_types.hh"
#include "BLI_rand.h"
#include "BLI_rand.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@ -54,6 +55,9 @@
#include "BLO_read_write.h"
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::RandomNumberGenerator;
static const char *HAIR_ATTR_POSITION = "position";
static const char *HAIR_ATTR_RADIUS = "radius";
@ -69,14 +73,22 @@ static void hair_init_data(ID *id)
MEMCPY_STRUCT_AFTER(hair, DNA_struct_default_get(Hair), id);
CustomData_reset(&hair->pdata);
CustomData_reset(&hair->cdata);
CustomData_reset(&hair->geometry.point_data);
CustomData_reset(&hair->geometry.curve_data);
CustomData_add_layer_named(&hair->geometry.point_data,
CD_PROP_FLOAT3,
CD_CALLOC,
nullptr,
hair->geometry.point_size,
HAIR_ATTR_POSITION);
CustomData_add_layer_named(&hair->geometry.point_data,
CD_PROP_FLOAT,
CD_CALLOC,
nullptr,
hair->geometry.point_size,
HAIR_ATTR_RADIUS);
CustomData_add_layer_named(
&hair->pdata, CD_PROP_FLOAT3, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_POSITION);
CustomData_add_layer_named(
&hair->pdata, CD_PROP_FLOAT, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_RADIUS);
CustomData_add_layer(&hair->cdata, CD_HAIRCURVE, CD_CALLOC, nullptr, hair->totcurve);
BKE_hair_update_customdata_pointers(hair);
hair_random(hair);
@ -88,11 +100,24 @@ static void hair_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, co
const Hair *hair_src = (const Hair *)id_src;
hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat));
hair_dst->geometry.point_size = hair_src->geometry.point_size;
hair_dst->geometry.curve_size = hair_src->geometry.curve_size;
const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, alloc_type, hair_dst->totpoint);
CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, alloc_type, hair_dst->totcurve);
CustomData_copy(&hair_src->geometry.point_data,
&hair_dst->geometry.point_data,
CD_MASK_ALL,
alloc_type,
hair_dst->geometry.point_size);
CustomData_copy(&hair_src->geometry.curve_data,
&hair_dst->geometry.curve_data,
CD_MASK_ALL,
alloc_type,
hair_dst->geometry.curve_size);
BKE_hair_update_customdata_pointers(hair_dst);
hair_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(hair_src->geometry.offsets));
hair_dst->batch_cache = nullptr;
}
@ -103,8 +128,10 @@ static void hair_free_data(ID *id)
BKE_hair_batch_cache_free(hair);
CustomData_free(&hair->pdata, hair->totpoint);
CustomData_free(&hair->cdata, hair->totcurve);
CustomData_free(&hair->geometry.point_data, hair->geometry.point_size);
CustomData_free(&hair->geometry.curve_data, hair->geometry.curve_size);
MEM_SAFE_FREE(hair->geometry.offsets);
MEM_SAFE_FREE(hair->mat);
}
@ -123,16 +150,30 @@ static void hair_blend_write(BlendWriter *writer, ID *id, const void *id_address
CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE];
CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE];
CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
CustomData_blend_write_prepare(
&hair->geometry.point_data, &players, players_buff, ARRAY_SIZE(players_buff));
CustomData_blend_write_prepare(
&hair->geometry.curve_data, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
/* Write LibData */
BLO_write_id_struct(writer, Hair, id_address, &hair->id);
BKE_id_blend_write(writer, &hair->id);
/* Direct data */
CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id);
CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id);
CustomData_blend_write(writer,
&hair->geometry.point_data,
players,
hair->geometry.point_size,
CD_MASK_ALL,
&hair->id);
CustomData_blend_write(writer,
&hair->geometry.curve_data,
clayers,
hair->geometry.curve_size,
CD_MASK_ALL,
&hair->id);
BLO_write_int32_array(writer, hair->geometry.curve_size + 1, hair->geometry.offsets);
BLO_write_pointer_array(writer, hair->totcol, hair->mat);
if (hair->adt) {
@ -155,10 +196,12 @@ static void hair_blend_read_data(BlendDataReader *reader, ID *id)
BKE_animdata_blend_read_data(reader, hair->adt);
/* Geometry */
CustomData_blend_read(reader, &hair->pdata, hair->totpoint);
CustomData_blend_read(reader, &hair->cdata, hair->totcurve);
CustomData_blend_read(reader, &hair->geometry.point_data, hair->geometry.point_size);
CustomData_blend_read(reader, &hair->geometry.curve_data, hair->geometry.point_size);
BKE_hair_update_customdata_pointers(hair);
BLO_read_int32_array(reader, hair->geometry.curve_size + 1, &hair->geometry.offsets);
/* Materials */
BLO_read_pointer_array(reader, (void **)&hair->mat);
}
@ -211,47 +254,51 @@ IDTypeInfo IDType_ID_HA = {
static void hair_random(Hair *hair)
{
CurvesGeometry &geometry = hair->geometry;
const int numpoints = 8;
hair->totcurve = 500;
hair->totpoint = hair->totcurve * numpoints;
geometry.curve_size = 500;
CustomData_realloc(&hair->pdata, hair->totpoint);
CustomData_realloc(&hair->cdata, hair->totcurve);
geometry.curve_size = 500;
geometry.point_size = geometry.curve_size * numpoints;
hair->geometry.offsets = (int *)MEM_calloc_arrayN(
hair->geometry.curve_size + 1, sizeof(int), __func__);
CustomData_realloc(&geometry.point_data, geometry.point_size);
CustomData_realloc(&geometry.curve_data, geometry.curve_size);
BKE_hair_update_customdata_pointers(hair);
RNG *rng = BLI_rng_new(0);
MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1};
MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size};
MutableSpan<float> radii{geometry.radius, geometry.point_size};
for (int i = 0; i < hair->totcurve; i++) {
HairCurve *curve = &hair->curves[i];
curve->firstpoint = i * numpoints;
curve->numpoints = numpoints;
float theta = 2.0f * M_PI * BLI_rng_get_float(rng);
float phi = saacosf(2.0f * BLI_rng_get_float(rng) - 1.0f);
float no[3] = {sinf(theta) * sinf(phi), cosf(theta) * sinf(phi), cosf(phi)};
normalize_v3(no);
float co[3];
copy_v3_v3(co, no);
float(*curve_co)[3] = hair->co + curve->firstpoint;
float *curve_radius = hair->radius + curve->firstpoint;
for (int key = 0; key < numpoints; key++) {
float t = key / (float)(numpoints - 1);
copy_v3_v3(curve_co[key], co);
curve_radius[key] = 0.02f * (1.0f - t);
float offset[3] = {2.0f * BLI_rng_get_float(rng) - 1.0f,
2.0f * BLI_rng_get_float(rng) - 1.0f,
2.0f * BLI_rng_get_float(rng) - 1.0f};
add_v3_v3(offset, no);
madd_v3_v3fl(co, offset, 1.0f / numpoints);
}
for (const int i : offsets.index_range()) {
geometry.offsets[i] = numpoints * i;
}
BLI_rng_free(rng);
RandomNumberGenerator rng;
for (int i = 0; i < geometry.curve_size; i++) {
const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
MutableSpan<float3> curve_positions = positions.slice(curve_range);
MutableSpan<float> curve_radii = radii.slice(curve_range);
const float theta = 2.0f * M_PI * rng.get_float();
const float phi = saacosf(2.0f * rng.get_float() - 1.0f);
float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)};
no = blender::math::normalize(no);
float3 co = no;
for (int key = 0; key < numpoints; key++) {
float t = key / (float)(numpoints - 1);
curve_positions[key] = co;
curve_radii[key] = 0.02f * (1.0f - t);
float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f;
co += (offset + no) / numpoints;
}
}
}
void *BKE_hair_add(Main *bmain, const char *name)
@ -276,9 +323,9 @@ BoundBox *BKE_hair_boundbox_get(Object *ob)
float min[3], max[3];
INIT_MINMAX(min, max);
float(*hair_co)[3] = hair->co;
float *hair_radius = hair->radius;
for (int a = 0; a < hair->totpoint; a++) {
float(*hair_co)[3] = hair->geometry.position;
float *hair_radius = hair->geometry.radius;
for (int a = 0; a < hair->geometry.point_size; a++) {
float *co = hair_co[a];
float radius = (hair_radius) ? hair_radius[a] : 0.0f;
const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
@ -295,12 +342,10 @@ BoundBox *BKE_hair_boundbox_get(Object *ob)
void BKE_hair_update_customdata_pointers(Hair *hair)
{
hair->co = (float(*)[3])CustomData_get_layer_named(
&hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION);
hair->radius = (float *)CustomData_get_layer_named(
&hair->pdata, CD_PROP_FLOAT, HAIR_ATTR_RADIUS);
hair->curves = (HairCurve *)CustomData_get_layer(&hair->cdata, CD_HAIRCURVE);
hair->mapping = (HairMaping *)CustomData_get_layer(&hair->cdata, CD_HAIRMAPPING);
hair->geometry.position = (float(*)[3])CustomData_get_layer_named(
&hair->geometry.point_data, CD_PROP_FLOAT3, HAIR_ATTR_POSITION);
hair->geometry.radius = (float *)CustomData_get_layer_named(
&hair->geometry.point_data, CD_PROP_FLOAT, HAIR_ATTR_RADIUS);
}
bool BKE_hair_customdata_required(Hair *UNUSED(hair), CustomDataLayer *layer)
@ -318,10 +363,18 @@ Hair *BKE_hair_new_for_eval(const Hair *hair_src, int totpoint, int totcurve)
hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat));
hair_dst->totcol = hair_src->totcol;
hair_dst->totpoint = totpoint;
hair_dst->totcurve = totcurve;
CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint);
CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, CD_CALLOC, totcurve);
hair_dst->geometry.point_size = totpoint;
hair_dst->geometry.curve_size = totcurve;
CustomData_copy(&hair_src->geometry.point_data,
&hair_dst->geometry.point_data,
CD_MASK_ALL,
CD_CALLOC,
totpoint);
CustomData_copy(&hair_src->geometry.curve_data,
&hair_dst->geometry.curve_data,
CD_MASK_ALL,
CD_CALLOC,
totcurve);
BKE_hair_update_customdata_pointers(hair_dst);
return hair_dst;
@ -373,12 +426,14 @@ static Hair *hair_evaluate_modifiers(struct Depsgraph *depsgraph,
}
/* Ensure we are not overwriting referenced data. */
CustomData_duplicate_referenced_layer_named(
&hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION, hair->totpoint);
CustomData_duplicate_referenced_layer_named(&hair->geometry.point_data,
CD_PROP_FLOAT3,
HAIR_ATTR_POSITION,
hair->geometry.point_size);
BKE_hair_update_customdata_pointers(hair);
/* Created deformed coordinates array on demand. */
mti->deformVerts(md, &mectx, nullptr, hair->co, hair->totpoint);
mti->deformVerts(md, &mectx, nullptr, hair->geometry.position, hair->geometry.point_size);
}
}

View File

@ -273,7 +273,8 @@ struct TileChangeset {
const int previous_chunk_len = chunk_dirty_flags_.size();
chunk_dirty_flags_.resize(chunk_len);
/* Fast exit. When the changeset was already empty no need to re-init the chunk_validity. */
/* Fast exit. When the changeset was already empty no need to
* re-initialize the chunk_validity. */
if (!has_dirty_chunks()) {
return;
}

View File

@ -1918,7 +1918,6 @@ void BKE_library_make_local(Main *bmain,
* but complicates slightly the pre-processing of relations between IDs at step 2... */
else if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) &&
ELEM(lib, NULL, id->lib) &&
!(GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) &&
((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING))) {
BLI_linklist_prepend_arena(&todo_ids, id, linklist_mem);
id->tag |= LIB_TAG_DOIT;
@ -1982,12 +1981,8 @@ void BKE_library_make_local(Main *bmain,
}
}
else {
/* In this specific case, we do want to make ID local even if it has no local usage yet...
* Note that for objects, we don't want proxy pointers to be cleared yet. This will happen
* down the road in this function.
*/
BKE_lib_id_make_local(
bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
/* In this specific case, we do want to make ID local even if it has no local usage yet... */
BKE_lib_id_make_local(bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY);
if (id->newid) {
if (GS(id->newid->name) == ID_OB) {
@ -2049,62 +2044,6 @@ void BKE_library_make_local(Main *bmain,
TIMEIT_VALUE_PRINT(make_local);
#endif
/* Step 5: proxy 'remapping' hack. */
for (LinkNode *it = copied_ids; it; it = it->next) {
ID *id = it->link;
/* Attempt to re-link copied proxy objects. This allows appending of an entire scene
* from another blend file into this one, even when that blend file contains proxified
* armatures that have local references. Since the proxified object needs to be linked
* (not local), this will only work when the "Localize all" checkbox is disabled.
* TL;DR: this is a dirty hack on top of an already weak feature (proxies). */
if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) {
Object *ob = (Object *)id;
Object *ob_new = (Object *)id->newid;
bool is_local = false, is_lib = false;
/* Proxies only work when the proxified object is linked-in from a library. */
if (!ID_IS_LINKED(ob->proxy)) {
CLOG_WARN(&LOG,
"proxy object %s will lose its link to %s, because the "
"proxified object is local.",
id->newid->name,
ob->proxy->id.name);
continue;
}
BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib);
/* We can only switch the proxy'ing to a made-local proxy if it is no longer
* referred to from a library. Not checking for local use; if new local proxy
* was not used locally would be a nasty bug! */
if (is_local || is_lib) {
CLOG_WARN(&LOG,
"made-local proxy object %s will lose its link to %s, "
"because the linked-in proxy is referenced (is_local=%i, is_lib=%i).",
id->newid->name,
ob->proxy->id.name,
is_local,
is_lib);
}
else {
/* we can switch the proxy'ing from the linked-in to the made-local proxy.
* BKE_object_make_proxy() shouldn't be used here, as it allocates memory that
* was already allocated by object_make_local() (which called BKE_object_copy). */
ob_new->proxy = ob->proxy;
ob_new->proxy_group = ob->proxy_group;
ob_new->proxy_from = ob->proxy_from;
ob_new->proxy->proxy_from = ob_new;
ob->proxy = ob->proxy_from = ob->proxy_group = NULL;
}
}
}
#ifdef DEBUG_TIME
printf("Step 5: Proxy 'remapping' hack: Done.\n");
TIMEIT_VALUE_PRINT(make_local);
#endif
/* This is probably more of a hack than something we should do here, but...
* Issue is, the whole copying + remapping done in complex cases above may leave pose-channels
* of armatures in complete invalid state (more precisely, the bone pointers of the

View File

@ -265,8 +265,8 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
}
for (id = last_remapped_id->next; id; id = id->next) {
/* Will tag 'never NULL' users of this ID too.
* Note that we cannot use BKE_libblock_unlink() here,
* since it would ignore indirect (and proxy!)
*
* NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect
* links, this can lead to nasty crashing here in second, actual deleting loop.
* Also, this will also flag users of deleted data that cannot be unlinked
* (object using deleted obdata, etc.), so that they also get deleted. */
@ -315,9 +315,9 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
}
/* Will tag 'never NULL' users of this ID too.
* Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect
* (and proxy!) links, this can lead to nasty crashing here in second,
* actual deleting loop.
*
* NOTE: #BKE_libblock_unlink() cannot be used here, since it would ignore indirect
* links, this can lead to nasty crashing here in second, actual deleting loop.
* Also, this will also flag users of deleted data that cannot be unlinked
* (object using deleted obdata, etc.), so that they also get deleted. */
BKE_libblock_remap_multiple_locked(bmain,

View File

@ -211,6 +211,7 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo
}
static ID *lib_override_library_create_from(Main *bmain,
Library *owner_library,
ID *reference_id,
const int lib_id_copy_flags)
{
@ -227,6 +228,12 @@ static ID *lib_override_library_create_from(Main *bmain,
}
id_us_min(local_id);
/* TODO: Handle this properly in LIB_NO_MAIN case as well (i.e. resync case). Or offload to
* generic ID copy code? */
if ((lib_id_copy_flags & LIB_ID_CREATE_NO_MAIN) == 0) {
local_id->lib = owner_library;
}
BKE_lib_override_library_init(local_id, reference_id);
/* NOTE: From liboverride perspective (and RNA one), shape keys are considered as local embedded
@ -281,7 +288,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
BLI_assert(reference_id != NULL);
BLI_assert(ID_IS_LINKED(reference_id));
ID *local_id = lib_override_library_create_from(bmain, reference_id, 0);
ID *local_id = lib_override_library_create_from(bmain, NULL, reference_id, 0);
/* We cannot allow automatic hierarchy resync on this ID, it is highly likely to generate a giant
* mess in case there are a lot of hidden, non-instantiated, non-properly organized dependencies.
* Ref T94650. */
@ -320,6 +327,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
}
bool BKE_lib_override_library_create_from_tag(Main *bmain,
Library *owner_library,
const Library *reference_library,
const bool do_no_main)
{
@ -351,7 +359,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
* This requires extra care further down the resync process,
* see: #BKE_lib_override_library_resync. */
reference_id->newid = lib_override_library_create_from(
bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
bmain, owner_library, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
if (reference_id->newid == NULL) {
success = false;
break;
@ -616,6 +624,35 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
}
}
static bool lib_override_linked_group_tag_collections_keep_tagged_check_recursive(
LibOverrideGroupTagData *data, Collection *collection)
{
/* NOTE: Collection's object cache (using bases, as returned by #BKE_collection_object_cache_get)
* is not usable here, as it may have become invalid from some previous operation and it should
* not be updated here. So instead only use collections' reliable 'raw' data to check if some
* object in the hierarchy of the given collection is still tagged for override. */
for (CollectionObject *collection_object = collection->gobject.first; collection_object != NULL;
collection_object = collection_object->next) {
Object *object = collection_object->ob;
if (object == NULL) {
continue;
}
if ((object->id.tag & data->tag) != 0) {
return true;
}
}
for (CollectionChild *collection_child = collection->children.first; collection_child != NULL;
collection_child = collection_child->next) {
if (lib_override_linked_group_tag_collections_keep_tagged_check_recursive(
data, collection_child->collection)) {
return true;
}
}
return false;
}
static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
@ -638,15 +675,8 @@ static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGr
if ((collection->id.tag & data->tag) == 0) {
continue;
}
bool keep_tagged = false;
const ListBase object_bases = BKE_collection_object_cache_get(collection);
LISTBASE_FOREACH (Base *, base, &object_bases) {
if ((base->object->id.tag & data->tag) != 0) {
keep_tagged = true;
break;
}
}
if (!keep_tagged) {
if (!lib_override_linked_group_tag_collections_keep_tagged_check_recursive(data, collection)) {
collection->id.tag &= ~data->tag;
}
}
@ -813,7 +843,10 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
lib_override_overrides_group_tag_recursive(data);
}
static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_root)
static bool lib_override_library_create_do(Main *bmain,
Scene *scene,
Library *owner_library,
ID *id_root)
{
BKE_main_relations_create(bmain, 0);
LibOverrideGroupTagData data = {.bmain = bmain,
@ -832,12 +865,13 @@ static bool lib_override_library_create_do(Main *bmain, Scene *scene, ID *id_roo
BKE_main_relations_free(bmain);
lib_override_group_tag_data_clear(&data);
return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false);
return BKE_lib_override_library_create_from_tag(bmain, owner_library, id_root->lib, false);
}
static void lib_override_library_create_post_process(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
const Library *owner_library,
ID *id_root,
ID *id_reference,
Collection *residual_storage,
@ -859,7 +893,8 @@ static void lib_override_library_create_post_process(Main *bmain,
/* 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 && !ID_IS_LINKED(id_root->newid)) {
if (!is_resync && id_root != NULL && id_root->newid != NULL &&
(!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) {
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
@ -904,7 +939,7 @@ static void lib_override_library_create_post_process(Main *bmain,
Collection *default_instantiating_collection = residual_storage;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
Object *ob_new = (Object *)ob->id.newid;
if (ob_new == NULL || ID_IS_LINKED(ob_new)) {
if (ob_new == NULL || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) {
continue;
}
@ -967,6 +1002,7 @@ static void lib_override_library_create_post_process(Main *bmain,
bool BKE_lib_override_library_create(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
Library *owner_library,
ID *id_root,
ID *id_reference,
ID **r_id_root_override)
@ -975,7 +1011,7 @@ bool BKE_lib_override_library_create(Main *bmain,
*r_id_root_override = NULL;
}
const bool success = lib_override_library_create_do(bmain, scene, id_root);
const bool success = lib_override_library_create_do(bmain, scene, owner_library, id_root);
if (!success) {
return success;
@ -986,7 +1022,7 @@ bool BKE_lib_override_library_create(Main *bmain,
}
lib_override_library_create_post_process(
bmain, scene, view_layer, id_root, id_reference, NULL, false);
bmain, scene, view_layer, owner_library, id_root, id_reference, NULL, false);
/* Cleanup. */
BKE_main_id_newptr_and_tag_clear(bmain);
@ -1011,116 +1047,6 @@ bool BKE_lib_override_library_template_create(struct ID *id)
return true;
}
bool BKE_lib_override_library_proxy_convert(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
Object *ob_proxy)
{
/* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is
* coming. */
Object *ob_proxy_group = ob_proxy->proxy_group;
const bool is_override_instancing_object = ob_proxy_group != NULL;
ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id :
&ob_proxy->proxy->id;
ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id;
/* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not
* sure this is a valid state, but for now just abort the overriding process. */
if (!ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
return false;
}
/* We manually convert the proxy object into a library override, further override handling will
* then be handled by `BKE_lib_override_library_create()` just as for a regular override
* creation.
*/
ob_proxy->proxy->id.tag |= LIB_TAG_DOIT;
ob_proxy->proxy->id.newid = &ob_proxy->id;
BKE_lib_override_library_init(&ob_proxy->id, &ob_proxy->proxy->id);
ob_proxy->proxy->proxy_from = NULL;
ob_proxy->proxy = ob_proxy->proxy_group = NULL;
DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE);
/* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created
* overrides.
* While this might not be 100% the desired behavior, it is likely to be the case most of the
* time. Ref: T91711. */
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
if (!ID_IS_LINKED(id_iter)) {
id_iter->tag |= LIB_TAG_DOIT;
}
}
FOREACH_MAIN_ID_END;
return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL);
}
static void lib_override_library_proxy_convert_do(Main *bmain,
Scene *scene,
Object *ob_proxy,
BlendFileReadReport *reports)
{
Object *ob_proxy_group = ob_proxy->proxy_group;
const bool is_override_instancing_object = ob_proxy_group != NULL;
const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy);
if (success) {
CLOG_INFO(&LOG,
4,
"Proxy object '%s' successfully converted to library overrides",
ob_proxy->id.name);
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
if (is_override_instancing_object) {
BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true);
}
reports->count.proxies_to_lib_overrides_success++;
}
}
void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports)
{
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
if (object->proxy_group == NULL) {
continue;
}
lib_override_library_proxy_convert_do(bmain, scene, object, reports);
}
FOREACH_SCENE_OBJECT_END;
FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
if (object->proxy == NULL) {
continue;
}
lib_override_library_proxy_convert_do(bmain, scene, object, reports);
}
FOREACH_SCENE_OBJECT_END;
}
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
if (ID_IS_LINKED(object)) {
if (object->proxy != NULL) {
CLOG_WARN(&LOG, "Did not try to convert linked proxy object '%s'", object->id.name);
reports->count.linked_proxies++;
}
continue;
}
if (object->proxy_group != NULL || object->proxy != NULL) {
CLOG_WARN(
&LOG, "Proxy object '%s' failed to be converted to library override", object->id.name);
reports->count.proxies_to_lib_overrides_failures++;
}
}
}
static void lib_override_library_remap(Main *bmain,
const ID *id_root_reference,
GHash *linkedref_to_old_override)
@ -1288,7 +1214,7 @@ static bool lib_override_library_resync(Main *bmain,
* override IDs (including within the old overrides themselves, since those are tagged too
* above). */
const bool success = BKE_lib_override_library_create_from_tag(
bmain, id_root_reference->lib, true);
bmain, NULL, id_root_reference->lib, true);
if (!success) {
return success;
@ -1502,6 +1428,7 @@ static bool lib_override_library_resync(Main *bmain,
lib_override_library_create_post_process(bmain,
scene,
view_layer,
NULL,
id_root_reference,
id_root,
override_resync_residual_storage,
@ -1926,7 +1853,7 @@ void BKE_lib_override_library_main_resync(Main *bmain,
/* 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);
bmain, scene, view_layer, NULL, 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);
@ -2979,10 +2906,6 @@ void BKE_lib_override_library_main_update(Main *bmain)
bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id)
{
if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) {
return true;
}
/* The only strong known case currently are objects used by override collections. */
/* TODO: There are most likely other cases... This may need to be addressed in a better way at
* some point. */

View File

@ -0,0 +1,176 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 by Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup bke
*/
#include "CLG_log.h"
#include "MEM_guardedalloc.h"
#include "BLI_linklist.h"
/* Required for proxy to liboverrides conversion code. */
#define DNA_DEPRECATED_ALLOW
#include "DNA_ID.h"
#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DEG_depsgraph.h"
#include "BKE_collection.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_main.h"
#include "BLO_readfile.h"
static CLG_LogRef LOG = {"bke.liboverride_proxy_conversion"};
bool BKE_lib_override_library_proxy_convert(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
Object *ob_proxy)
{
/* `proxy_group`, if defined, is the empty instantiating the collection from which the proxy is
* coming. */
Object *ob_proxy_group = ob_proxy->proxy_group;
const bool is_override_instancing_object = ob_proxy_group != NULL;
ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id :
&ob_proxy->proxy->id;
ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id;
/* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not
* sure this is a valid state, but for now just abort the overriding process. */
if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_root)) {
if (ob_proxy->proxy != NULL) {
ob_proxy->proxy->proxy_from = NULL;
}
id_us_min((ID *)ob_proxy->proxy);
ob_proxy->proxy = ob_proxy->proxy_group = NULL;
return false;
}
/* We manually convert the proxy object into a library override, further override handling will
* then be handled by `BKE_lib_override_library_create()` just as for a regular override
* creation.
*/
ob_proxy->proxy->id.tag |= LIB_TAG_DOIT;
ob_proxy->proxy->id.newid = &ob_proxy->id;
BKE_lib_override_library_init(&ob_proxy->id, &ob_proxy->proxy->id);
ob_proxy->proxy->proxy_from = NULL;
ob_proxy->proxy = ob_proxy->proxy_group = NULL;
DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE);
/* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created
* overrides. Also do that for the IDs from the same lib as the proxy in case it is linked.
* While this might not be 100% the desired behavior, it is likely to be the case most of the
* time. Ref: T91711. */
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
if (!ID_IS_LINKED(id_iter) || id_iter->lib == ob_proxy->id.lib) {
id_iter->tag |= LIB_TAG_DOIT;
}
}
FOREACH_MAIN_ID_END;
return BKE_lib_override_library_create(
bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_reference, NULL);
}
static void lib_override_library_proxy_convert_do(Main *bmain,
Scene *scene,
Object *ob_proxy,
BlendFileReadReport *reports)
{
Object *ob_proxy_group = ob_proxy->proxy_group;
const bool is_override_instancing_object = ob_proxy_group != NULL;
const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy);
if (success) {
CLOG_INFO(&LOG,
4,
"Proxy object '%s' successfully converted to library overrides",
ob_proxy->id.name);
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
if (is_override_instancing_object) {
BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true);
}
reports->count.proxies_to_lib_overrides_success++;
}
}
void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports)
{
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
LinkNodePair proxy_objects = {NULL};
FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
if (object->proxy_group != NULL) {
BLI_linklist_append(&proxy_objects, object);
}
}
FOREACH_SCENE_OBJECT_END;
FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
if (object->proxy != NULL && object->proxy_group == NULL) {
BLI_linklist_append(&proxy_objects, object);
}
}
FOREACH_SCENE_OBJECT_END;
for (LinkNode *proxy_object_iter = proxy_objects.list; proxy_object_iter != NULL;
proxy_object_iter = proxy_object_iter->next) {
Object *proxy_object = proxy_object_iter->link;
lib_override_library_proxy_convert_do(bmain, scene, proxy_object, reports);
}
BLI_linklist_free(proxy_objects.list, NULL);
}
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
if (object->proxy_group != NULL || object->proxy != NULL) {
if (ID_IS_LINKED(object)) {
CLOG_WARN(&LOG,
"Linked proxy object '%s' from '%s' failed to be converted to library override",
object->id.name + 2,
object->id.lib->filepath);
}
else {
CLOG_WARN(&LOG,
"Proxy object '%s' failed to be converted to library override",
object->id.name + 2);
}
reports->count.proxies_to_lib_overrides_failures++;
if (object->proxy != NULL) {
object->proxy->proxy_from = NULL;
}
id_us_min((ID *)object->proxy);
object->proxy = object->proxy_group = NULL;
}
}
}

View File

@ -517,7 +517,7 @@ static int foreach_libblock_id_users_callback(LibraryIDLinkCallbackData *cb_data
IDUsersIter *iter = cb_data->user_data;
if (*id_p) {
/* 'Loopback' ID pointers (the ugly 'from' ones, Object->proxy_from and Key->from).
/* 'Loopback' ID pointers (the ugly 'from' ones, like Key->from).
* Those are not actually ID usage, we can ignore them here.
*/
if (cb_flag & IDWALK_CB_LOOPBACK) {
@ -768,7 +768,7 @@ static int foreach_libblock_used_linked_data_tag_clear_cb(LibraryIDLinkCallbackD
bool *is_changed = cb_data->user_data;
if (*id_p) {
/* The infamous 'from' pointers (Key.from, Object.proxy_from, ...).
/* The infamous 'from' pointers (Key.from, ...).
* those are not actually ID usage, so we ignore them here. */
if (cb_flag & IDWALK_CB_LOOPBACK) {
return IDWALK_RET_NOP;

View File

@ -91,26 +91,18 @@ enum {
ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1, /* There was some skipped 'user_one' usages of old_id. */
};
static void foreach_libblock_remap_callback_skip(const ID *id_owner,
ID **id_ptr,
static void foreach_libblock_remap_callback_skip(const ID *UNUSED(id_owner),
ID **UNUSED(id_ptr),
IDRemap *id_remap_data,
const int cb_flag,
const bool is_indirect,
const bool is_reference,
const bool is_never_null,
const bool is_obj,
const bool UNUSED(is_obj),
const bool is_obj_editmode)
{
if (is_indirect) {
id_remap_data->skipped_indirect++;
if (is_obj) {
Object *ob = (Object *)id_owner;
if (ob->data == *id_ptr && ob->proxy != NULL) {
/* And another 'Proudly brought to you by Proxy Hell' hack!
* This will allow us to avoid clearing 'LIB_EXTERN' flag of obdata of proxies... */
id_remap_data->skipped_direct++;
}
}
}
else if (is_never_null || is_obj_editmode || is_reference) {
id_remap_data->skipped_direct++;
@ -136,8 +128,7 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner,
const int cb_flag,
const bool is_indirect,
const bool is_never_null,
const bool force_user_refcount,
const bool is_obj_proxy)
const bool force_user_refcount)
{
if (!is_never_null) {
*id_ptr = new_id;
@ -170,16 +161,9 @@ static void foreach_libblock_remap_callback_apply(ID *id_owner,
/* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET)
* are assumed to be set as needed, that extra user is processed in final handling. */
}
if (!is_indirect || is_obj_proxy) {
if (!is_indirect) {
id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT;
}
/* We need to remap proxy_from pointer of remapped proxy... sigh. */
if (is_obj_proxy && new_id != NULL) {
Object *ob = (Object *)id_owner;
if (ob->proxy == (Object *)new_id) {
ob->proxy->proxy_from = ob;
}
}
}
static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
@ -221,12 +205,9 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
const bool is_reference = (cb_flag & IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE) != 0;
const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0;
const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0;
/* NOTE: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct,
* on the other hand since they get reset to lib data on file open/reload it is indirect too.
* Edit Mode is also a 'skip direct' case. */
const bool is_obj = (GS(id_owner->name) == ID_OB);
const bool is_obj_proxy = (is_obj &&
(((Object *)id_owner)->proxy || ((Object *)id_owner)->proxy_group));
/* NOTE: Edit Mode is a 'skip direct' case, unless specifically requested, obdata should not be
* remapped in this situation. */
const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner) &&
(id_remap_data->flag & ID_REMAP_FORCE_OBDATA_IN_EDITMODE) == 0);
const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) &&
@ -281,8 +262,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
cb_flag,
is_indirect,
is_never_null,
force_user_refcount,
is_obj_proxy);
force_user_refcount);
}
return IDWALK_RET_NOP;
@ -430,10 +410,7 @@ static void libblock_remap_data(
Main *bmain, ID *id, ID *old_id, ID *new_id, const short remap_flags, IDRemap *r_id_remap_data)
{
IDRemap id_remap_data;
const int foreach_id_flags = ((remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ?
IDWALK_NO_INDIRECT_PROXY_DATA_USAGE :
IDWALK_NOP) |
((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
const int foreach_id_flags = ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
IDWALK_DO_INTERNAL_RUNTIME_POINTERS :
IDWALK_NOP);

View File

@ -34,19 +34,29 @@
#include "MEM_guardedalloc.h"
/* General note on iterating verts/loops/edges/polys and end mode.
*
* The edit mesh pointer is set for both final and cage meshes in both cases when there are
* modifiers applied and not. This helps consistency of checks in the draw manager, where the
* existence of the edit mesh pointer does not depend on object configuration.
*
* For the iterating, however, we need to follow the `CD_ORIGINDEX` code paths when there are
* modifiers applied on the cage. In the code terms it means that the check for the edit mode code
* path needs to consist of both edit mesh and edit data checks. */
void BKE_mesh_foreach_mapped_vert(
Mesh *mesh,
void (*func)(void *userData, int index, const float co[3], const float no[3]),
void *userData,
MeshForeachFlag flag)
{
if (mesh->edit_mesh != NULL) {
if (mesh->edit_mesh != NULL && mesh->runtime.edit_data != NULL) {
BMEditMesh *em = mesh->edit_mesh;
BMesh *bm = em->bm;
BMIter iter;
BMVert *eve;
int i;
if (mesh->runtime.edit_data != NULL && mesh->runtime.edit_data->vertexCos != NULL) {
if (mesh->runtime.edit_data->vertexCos != NULL) {
const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos;
const float(*vertexNos)[3];
if (flag & MESH_FOREACH_USE_NORMAL) {
@ -100,13 +110,13 @@ void BKE_mesh_foreach_mapped_edge(
void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]),
void *userData)
{
if (mesh->edit_mesh != NULL) {
if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) {
BMEditMesh *em = mesh->edit_mesh;
BMesh *bm = em->bm;
BMIter iter;
BMEdge *eed;
int i;
if (mesh->runtime.edit_data != NULL && mesh->runtime.edit_data->vertexCos != NULL) {
if (mesh->runtime.edit_data->vertexCos != NULL) {
const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos;
BM_mesh_elem_index_ensure(bm, BM_VERT);
@ -158,14 +168,13 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh,
/* We can't use dm->getLoopDataLayout(dm) here,
* we want to always access dm->loopData, EditDerivedBMesh would
* return loop data from bmesh itself. */
if (mesh->edit_mesh != NULL) {
if (mesh->edit_mesh != NULL && mesh->runtime.edit_data) {
BMEditMesh *em = mesh->edit_mesh;
BMesh *bm = em->bm;
BMIter iter;
BMFace *efa;
const float(*vertexCos)[3] = mesh->runtime.edit_data ? mesh->runtime.edit_data->vertexCos :
NULL;
const float(*vertexCos)[3] = mesh->runtime.edit_data->vertexCos;
/* XXX: investigate using EditMesh data. */
const float(*lnors)[3] = (flag & MESH_FOREACH_USE_NORMAL) ?

View File

@ -290,6 +290,16 @@ ModifierData *BKE_modifiers_findby_name(const Object *ob, const char *name)
return BLI_findstring(&(ob->modifiers), name, offsetof(ModifierData, name));
}
ModifierData *BKE_modifiers_findby_session_uuid(const Object *ob, const SessionUUID *session_uuid)
{
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (BLI_session_uuid_is_equal(&md->session_uuid, session_uuid)) {
return md;
}
}
return NULL;
}
void BKE_modifiers_clear_errors(Object *ob)
{
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
@ -439,9 +449,7 @@ void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_for
#ifndef NDEBUG
if ((md->mode & eModifierMode_Virtual) == 0) {
/* Ensure correct object is passed in. */
const Object *ob_orig = (Object *)DEG_get_original_id((ID *)&ob->id);
const ModifierData *md_orig = md->orig_modifier_data ? md->orig_modifier_data : md;
BLI_assert(BLI_findindex(&ob_orig->modifiers, md_orig) != -1);
BLI_assert(BKE_modifier_get_original(ob, md) != NULL);
}
#endif
@ -1052,12 +1060,10 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval,
return me;
}
ModifierData *BKE_modifier_get_original(ModifierData *md)
ModifierData *BKE_modifier_get_original(const Object *object, ModifierData *md)
{
if (md->orig_modifier_data == NULL) {
return md;
}
return md->orig_modifier_data;
const Object *object_orig = DEG_get_original_object((Object *)object);
return BKE_modifiers_findby_session_uuid(object_orig, &md->session_uuid);
}
struct ModifierData *BKE_modifier_get_evaluated(Depsgraph *depsgraph,
@ -1068,7 +1074,7 @@ struct ModifierData *BKE_modifier_get_evaluated(Depsgraph *depsgraph,
if (object_eval == object) {
return md;
}
return BKE_modifiers_findby_name(object_eval, md->name);
return BKE_modifiers_findby_session_uuid(object_eval, &md->session_uuid);
}
void BKE_modifier_check_uuids_unique_and_report(const Object *object)
@ -1182,8 +1188,8 @@ void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase)
#if 0
CollisionModifierData *collmd = (CollisionModifierData *)md;
// TODO: CollisionModifier should use pointcache
// + have proper reset events before enabling this
/* TODO: CollisionModifier should use pointcache
* + have proper reset events before enabling this. */
writestruct(wd, DATA, MVert, collmd->numverts, collmd->x);
writestruct(wd, DATA, MVert, collmd->numverts, collmd->xnew);
writestruct(wd, DATA, MFace, collmd->numfaces, collmd->mfaces);

View File

@ -4503,6 +4503,8 @@ static void registerCompositNodes()
register_node_type_cmp_sepycca();
register_node_type_cmp_combycca();
register_node_type_cmp_premulkey();
register_node_type_cmp_separate_xyz();
register_node_type_cmp_combine_xyz();
register_node_type_cmp_diff_matte();
register_node_type_cmp_distance_matte();

View File

@ -325,45 +325,6 @@ static void object_free_data(ID *id)
BKE_previewimg_free(&ob->preview);
}
static void object_make_local(Main *bmain, ID *id, const int flags)
{
if (!ID_IS_LINKED(id)) {
return;
}
Object *ob = (Object *)id;
const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0;
bool force_local, force_copy;
BKE_lib_id_make_local_generic_action_define(bmain, id, flags, &force_local, &force_copy);
if (force_local) {
BKE_lib_id_clear_library_data(bmain, &ob->id, flags);
BKE_lib_id_expand_local(bmain, &ob->id, flags);
if (clear_proxy) {
if (ob->proxy_from != nullptr) {
ob->proxy_from->proxy = nullptr;
ob->proxy_from->proxy_group = nullptr;
}
ob->proxy = ob->proxy_from = ob->proxy_group = nullptr;
}
}
else if (force_copy) {
Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id);
id_us_min(&ob_new->id);
ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = nullptr;
/* setting newid is mandatory for complex make_lib_local logic... */
ID_NEW_SET(ob, ob_new);
if (!lib_local) {
BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE);
}
}
}
static void library_foreach_modifiersForeachIDLink(void *user_data,
Object *UNUSED(object),
ID **id_pointer,
@ -419,51 +380,25 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
{
Object *object = (Object *)id;
/* Object is special, proxies make things hard... */
const int proxy_cb_flag = ((BKE_lib_query_foreachid_process_flags_get(data) &
IDWALK_NO_INDIRECT_PROXY_DATA_USAGE) == 0 &&
(object->proxy || object->proxy_group)) ?
IDWALK_CB_INDIRECT_USAGE :
0;
/* object data special case */
if (object->type == OB_EMPTY) {
/* empty can have nullptr or Image */
BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, proxy_cb_flag | IDWALK_CB_USER);
BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, IDWALK_CB_USER);
}
else {
/* when set, this can't be nullptr */
if (object->data) {
BKE_LIB_FOREACHID_PROCESS_ID(
data, object->data, proxy_cb_flag | IDWALK_CB_USER | IDWALK_CB_NEVER_NULL);
BKE_LIB_FOREACHID_PROCESS_ID(data, object->data, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL);
}
}
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF);
/* object->proxy is refcounted, but not object->proxy_group... *sigh* */
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP);
/* Special case!
* Since this field is set/owned by 'user' of this ID (and not ID itself),
* it is only indirect usage if proxy object is linked... Twisted. */
{
const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override(
data,
(object->proxy_from != nullptr && ID_IS_LINKED(object->proxy_from)) ?
IDWALK_CB_INDIRECT_USAGE :
0,
true);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF);
BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true);
}
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER);
for (int i = 0; i < object->totcol; i++) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], IDWALK_CB_USER);
}
/* Note that ob->gpd is deprecated, so no need to handle it here. */
@ -476,8 +411,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
/* Note that ob->effect is deprecated, so no need to handle it here. */
if (object->pose) {
const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override(
data, proxy_cb_flag, false);
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
data,
@ -492,7 +425,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_constraints_id_loop(
&pchan->constraints, library_foreach_constraintObjectLooper, data));
}
BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true);
}
if (object->rigidbody_constraint) {
@ -627,9 +559,6 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre
bArmature *arm = nullptr;
if (ob->type == OB_ARMATURE) {
arm = (bArmature *)ob->data;
if (arm && ob->pose && arm->act_bone) {
BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone));
}
}
BKE_pose_blend_write(writer, ob->pose, arm);
@ -1305,7 +1234,7 @@ IDTypeInfo IDType_ID_OB = {
/* init_data */ object_init_data,
/* copy_data */ object_copy_data,
/* free_data */ object_free_data,
/* make_local */ object_make_local,
/* make_local */ nullptr,
/* foreach_id */ object_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ object_foreach_path,
@ -2873,161 +2802,6 @@ bool BKE_object_obdata_is_libdata(const Object *ob)
return (ob && ob->data && ID_IS_LINKED(ob->data));
}
/* -------------------------------------------------------------------- */
/** \name Object Proxy API
* \{ */
/* when you make proxy, ensure the exposed layers are extern */
static void armature_set_id_extern(Object *ob)
{
bArmature *arm = (bArmature *)ob->data;
unsigned int lay = arm->layer_protected;
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
if (!(pchan->bone->layer & lay)) {
id_lib_extern((ID *)pchan->custom);
}
}
}
void BKE_object_copy_proxy_drivers(Object *ob, Object *target)
{
if ((target->adt) && (target->adt->drivers.first)) {
/* add new animdata block */
if (!ob->adt) {
ob->adt = BKE_animdata_ensure_id(&ob->id);
}
/* make a copy of all the drivers (for now), then correct any links that need fixing */
BKE_fcurves_free(&ob->adt->drivers);
BKE_fcurves_copy(&ob->adt->drivers, &target->adt->drivers);
LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->drivers) {
ChannelDriver *driver = fcu->driver;
LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
/* all drivers */
DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
if (dtar->id) {
if ((Object *)dtar->id == target) {
dtar->id = (ID *)ob;
}
else {
/* only on local objects because this causes indirect links
* 'a -> b -> c', blend to point directly to a.blend
* when a.blend has a proxy that's linked into `c.blend`. */
if (!ID_IS_LINKED(ob)) {
id_lib_extern((ID *)dtar->id);
}
}
}
}
DRIVER_TARGETS_LOOPER_END;
}
}
}
}
void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob)
{
/* paranoia checks */
if (ID_IS_LINKED(ob) || !ID_IS_LINKED(target)) {
CLOG_ERROR(&LOG, "cannot make proxy");
return;
}
ob->proxy = target;
id_us_plus(&target->id);
ob->proxy_group = cob;
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
DEG_id_tag_update(&target->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
/* copy transform
* - cob means this proxy comes from a collection, just apply the matrix
* so the object won't move from its dupli-transform.
*
* - no cob means this is being made from a linked object,
* this is closer to making a copy of the object - in-place. */
if (cob) {
ob->rotmode = target->rotmode;
mul_m4_m4m4(ob->obmat, cob->obmat, target->obmat);
if (cob->instance_collection) { /* should always be true */
float tvec[3];
mul_v3_mat3_m4v3(tvec, ob->obmat, cob->instance_collection->instance_offset);
sub_v3_v3(ob->obmat[3], tvec);
}
BKE_object_apply_mat4(ob, ob->obmat, false, true);
}
else {
BKE_object_transform_copy(ob, target);
ob->parent = target->parent; /* libdata */
copy_m4_m4(ob->parentinv, target->parentinv);
}
/* copy animdata stuff - drivers only for now... */
BKE_object_copy_proxy_drivers(ob, target);
/* skip constraints? */
/* FIXME: this is considered by many as a bug */
/* set object type and link to data */
ob->type = target->type;
ob->data = target->data;
id_us_plus((ID *)ob->data); /* ensures lib data becomes LIB_TAG_EXTERN */
/* copy material and index information */
ob->actcol = ob->totcol = 0;
if (ob->mat) {
MEM_freeN(ob->mat);
}
if (ob->matbits) {
MEM_freeN(ob->matbits);
}
ob->mat = nullptr;
ob->matbits = nullptr;
if ((target->totcol) && (target->mat) && OB_TYPE_SUPPORT_MATERIAL(ob->type)) {
int i;
ob->actcol = target->actcol;
ob->totcol = target->totcol;
ob->mat = (Material **)MEM_dupallocN(target->mat);
ob->matbits = (char *)MEM_dupallocN(target->matbits);
for (i = 0; i < target->totcol; i++) {
/* don't need to run BKE_object_materials_test
* since we know this object is new and not used elsewhere */
id_us_plus((ID *)ob->mat[i]);
}
}
/* type conversions */
if (target->type == OB_ARMATURE) {
copy_object_pose(ob, target, 0); /* data copy, object pointers in constraints */
BKE_pose_rest(ob->pose, false); /* clear all transforms in channels */
BKE_pose_rebuild(bmain, ob, (bArmature *)ob->data, true); /* set all internal links */
armature_set_id_extern(ob);
}
else if (target->type == OB_EMPTY) {
ob->empty_drawtype = target->empty_drawtype;
ob->empty_drawsize = target->empty_drawsize;
}
/* copy IDProperties */
if (ob->id.properties) {
IDP_FreeProperty(ob->id.properties);
ob->id.properties = nullptr;
}
if (target->id.properties) {
ob->id.properties = IDP_CopyProperty(target->id.properties);
}
/* copy drawtype info */
ob->dt = target->dt;
}
void BKE_object_obdata_size_init(struct Object *ob, const float size)
{
/* apply radius as a scale to types that support it */
@ -3069,8 +2843,6 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size)
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Object Matrix Get/Set API
* \{ */
@ -4186,7 +3958,11 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
/* pass */
}
else {
BoundBox *bb = BKE_object_boundbox_get(dob->ob);
Object temp_ob = *dob->ob;
/* Do not modify the original boundbox. */
temp_ob.runtime.bb = nullptr;
BKE_object_replace_data_on_shallow_copy(&temp_ob, dob->ob_data);
BoundBox *bb = BKE_object_boundbox_get(&temp_ob);
if (bb) {
int i;
@ -4198,6 +3974,8 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
ok = true;
}
MEM_SAFE_FREE(temp_ob.runtime.bb);
}
}
free_object_duplilist(lb); /* does restore */
@ -4356,33 +4134,10 @@ void BKE_object_tfm_restore(Object *ob, void *obtfm_pt)
/** \name Object Evaluation/Update API
* \{ */
static void object_handle_update_proxy(Depsgraph *depsgraph,
Scene *scene,
Object *object,
const bool do_proxy_update)
{
/* The case when this is a collection proxy, object_update is called in collection.c */
if (object->proxy == nullptr) {
return;
}
/* set pointer in library proxy target, for copying, but restore it */
object->proxy->proxy_from = object;
// printf("set proxy pointer for later collection stuff %s\n", ob->id.name);
/* the no-group proxy case, we call update */
if (object->proxy_group == nullptr) {
if (do_proxy_update) {
// printf("call update, lib ob %s proxy %s\n", ob->proxy->id.name, ob->id.name);
BKE_object_handle_update(depsgraph, scene, object->proxy);
}
}
}
void BKE_object_handle_update_ex(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
RigidBodyWorld *rbw,
const bool do_proxy_update)
RigidBodyWorld *rbw)
{
const ID *object_data = (ID *)ob->data;
const bool recalc_object = (ob->id.recalc & ID_RECALC_ALL) != 0;
@ -4390,7 +4145,6 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
((object_data->recalc & ID_RECALC_ALL) != 0) :
false;
if (!recalc_object && !recalc_data) {
object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update);
return;
}
/* Speed optimization for animation lookups. */
@ -4419,22 +4173,17 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
printf("recalcob %s\n", ob->id.name + 2);
}
/* Handle proxy copy for target. */
if (!BKE_object_eval_proxy_copy(depsgraph, ob)) {
BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr);
}
BKE_object_where_is_calc_ex(depsgraph, scene, rbw, ob, nullptr);
}
if (recalc_data) {
BKE_object_handle_data_update(depsgraph, scene, ob);
}
object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update);
}
void BKE_object_handle_update(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr, true);
BKE_object_handle_update_ex(depsgraph, scene, ob, nullptr);
}
void BKE_object_sculpt_data_create(Object *ob)

View File

@ -190,16 +190,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
break;
}
case OB_ARMATURE:
if (ID_IS_LINKED(ob) && ob->proxy_from) {
if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) {
printf("Proxy copy error, lib Object: %s proxy Object: %s\n",
ob->id.name + 2,
ob->proxy_from->id.name + 2);
}
}
else {
BKE_pose_where_is(depsgraph, scene, ob);
}
BKE_pose_where_is(depsgraph, scene, ob);
break;
case OB_MBALL:
@ -311,33 +302,9 @@ void BKE_object_sync_to_original(Depsgraph *depsgraph, Object *object)
object_sync_boundbox_to_original(object_orig, object);
}
bool BKE_object_eval_proxy_copy(Depsgraph *depsgraph, Object *object)
void BKE_object_eval_uber_transform(Depsgraph *UNUSED(depsgraph), Object *UNUSED(object))
{
/* Handle proxy copy for target, */
if (ID_IS_LINKED(object) && object->proxy_from) {
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
if (object->proxy_from->proxy_group) {
/* Transform proxy into group space. */
Object *obg = object->proxy_from->proxy_group;
float imat[4][4];
invert_m4_m4(imat, obg->obmat);
mul_m4_m4m4(object->obmat, imat, object->proxy_from->obmat);
/* Should always be true. */
if (obg->instance_collection) {
add_v3_v3(object->obmat[3], obg->instance_collection->instance_offset);
}
}
else {
copy_m4_m4(object->obmat, object->proxy_from->obmat);
}
return true;
}
return false;
}
void BKE_object_eval_uber_transform(Depsgraph *depsgraph, Object *object)
{
BKE_object_eval_proxy_copy(depsgraph, object);
return;
}
void BKE_object_data_batch_cache_dirty_tag(ID *object_data)

View File

@ -1648,7 +1648,6 @@ static void sculpt_update_object(Depsgraph *depsgraph,
ss->totvert = me->totvert;
ss->totpoly = me->totpoly;
ss->totfaces = me->totpoly;
ss->vert_normals = BKE_mesh_vertex_normals_ensure(me);
ss->mvert = me->mvert;
ss->mpoly = me->mpoly;
ss->mloop = me->mloop;

View File

@ -1007,6 +1007,28 @@ typedef struct PBVHUpdateData {
bool show_sculpt_face_sets;
} PBVHUpdateData;
static void pbvh_update_normals_clear_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
{
PBVHUpdateData *data = userdata;
PBVH *pbvh = data->pbvh;
PBVHNode *node = data->nodes[n];
float(*vnors)[3] = data->vnors;
if (node->flag & PBVH_UpdateNormals) {
const int *verts = node->vert_indices;
const int totvert = node->uniq_verts;
for (int i = 0; i < totvert; i++) {
const int v = verts[i];
const MVert *mvert = &pbvh->verts[v];
if (mvert->flag & ME_VERT_PBVH_UPDATE) {
zero_v3(vnors[v]);
}
}
}
}
static void pbvh_update_normals_accum_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
@ -1107,6 +1129,8 @@ static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode)
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
/* Zero normals before accumulation. */
BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_clear_task_cb, &settings);
BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings);
BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings);
}

View File

@ -2522,7 +2522,7 @@ void BKE_scene_update_sound(Depsgraph *depsgraph, Main *bmain)
Scene *scene = DEG_get_evaluated_scene(depsgraph);
const int recalc = scene->id.recalc;
BKE_sound_ensure_scene(scene);
if (recalc & ID_RECALC_AUDIO_SEEK) {
if (recalc & ID_RECALC_FRAME_CHANGE) {
BKE_sound_seek_scene(bmain, scene);
}
if (recalc & ID_RECALC_AUDIO_FPS) {

View File

@ -726,14 +726,12 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
if (codec_id == AV_CODEC_ID_VP9) {
if (rd->im_format.planes == R_IMF_PLANES_RGBA) {
c->pix_fmt = AV_PIX_FMT_YUVA420P;
}
if (codec_id == AV_CODEC_ID_VP9 && rd->im_format.planes == R_IMF_PLANES_RGBA) {
c->pix_fmt = AV_PIX_FMT_YUVA420P;
}
/* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */
if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && context->ffmpeg_crf == 0) {
else if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) &&
context->ffmpeg_crf == 0) {
/* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */
c->pix_fmt = AV_PIX_FMT_YUV444P;
}

View File

@ -358,7 +358,9 @@ constexpr int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) con
constexpr int64_t StringRefBase::find_first_of(char c, int64_t pos) const
{
return this->find_first_of(StringRef(&c, 1), pos);
BLI_assert(pos >= 0);
return index_or_npos_to_int64(
std::string_view(*this).find_first_of(c, static_cast<size_t>(pos)));
}
constexpr int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const
@ -370,7 +372,9 @@ constexpr int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) cons
constexpr int64_t StringRefBase::find_last_of(char c, int64_t pos) const
{
return this->find_last_of(StringRef(&c, 1), pos);
BLI_assert(pos >= 0);
return index_or_npos_to_int64(
std::string_view(*this).find_last_of(c, static_cast<size_t>(pos)));
}
constexpr int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const
@ -382,7 +386,9 @@ constexpr int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos)
constexpr int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const
{
return this->find_first_not_of(StringRef(&c, 1), pos);
BLI_assert(pos >= 0);
return index_or_npos_to_int64(
std::string_view(*this).find_first_not_of(c, static_cast<size_t>(pos)));
}
constexpr int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const
@ -394,7 +400,9 @@ constexpr int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos)
constexpr int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const
{
return this->find_last_not_of(StringRef(&c, 1), pos);
BLI_assert(pos >= 0);
return index_or_npos_to_int64(
std::string_view(*this).find_last_not_of(c, static_cast<size_t>(pos)));
}
constexpr StringRef StringRefBase::trim() const

View File

@ -570,6 +570,10 @@ class VectorSet {
if (this->size() == 0) {
try {
slots_.reinitialize(total_slots);
if (keys_ != nullptr) {
this->deallocate_keys_array(keys_);
keys_ = nullptr;
}
keys_ = this->allocate_keys_array(usable_slots);
}
catch (...) {

View File

@ -271,4 +271,14 @@ TEST(vector_set, LookupKey)
EXPECT_EQ(set.lookup_key_ptr("a"), set.lookup_key_ptr_as("a"));
}
TEST(vector_set, GrowWhenEmpty)
{
/* Tests that the internal keys array is freed correctly when growing an empty set. */
VectorSet<int> set;
set.add(4);
set.remove(4);
EXPECT_TRUE(set.is_empty());
set.reserve(100);
}
} // namespace blender::tests

View File

@ -117,8 +117,6 @@ typedef struct BlendFileReadReport {
/* Number of root override IDs that were resynced. */
int resynced_lib_overrides;
/* Number of (non-converted) linked proxies. */
int linked_proxies;
/* Number of proxies converted to library overrides. */
int proxies_to_lib_overrides_success;
/* Number of proxies that failed to convert to library overrides. */

View File

@ -419,9 +419,6 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain,
fd->skip_flags = params->skip_flags;
BLI_strncpy(fd->relabase, filename, sizeof(fd->relabase));
/* clear ob->proxy_from pointers in old main */
blo_clear_proxy_pointers_from_lib(oldmain);
/* separate libraries from old main */
blo_split_main(&old_mainlist, oldmain);
/* add the library pointers in oldmap lookup */

View File

@ -1580,15 +1580,6 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist,
}
}
void blo_clear_proxy_pointers_from_lib(Main *oldmain)
{
LISTBASE_FOREACH (Object *, ob, &oldmain->objects) {
if (ID_IS_LINKED(ob) && ob->proxy_from != NULL && !ID_IS_LINKED(ob->proxy_from)) {
ob->proxy_from = NULL;
}
}
}
/* XXX disabled this feature - packed files also belong in temp saves and quit.blend,
* to make restore work. */
@ -3207,18 +3198,8 @@ static void read_libblock_undo_restore_identical(
id_old->recalc |= direct_link_id_restore_recalc_exceptions(id_old);
id_old->recalc_after_undo_push = 0;
/* As usual, proxies require some special love...
* In `blo_clear_proxy_pointers_from_lib()` we clear all `proxy_from` pointers to local IDs, for
* undo. This is required since we do not re-read linked data in that case, so we also do not
* re-'lib_link' their pointers.
* Those `proxy_from` pointers are then re-defined properly when lib_linking the newly read local
* object. However, in case of re-used data 'as-is', we never lib_link it again, so we have to
* fix those backward pointers here. */
if (GS(id_old->name) == ID_OB) {
Object *ob = (Object *)id_old;
if (ob->proxy != NULL) {
ob->proxy->proxy_from = ob;
}
/* For undo we stay in object mode during undo presses, so keep editmode disabled for re-used
* data-blocks too. */
ob->mode &= ~OB_MODE_EDIT;

View File

@ -144,14 +144,6 @@ FileData *blo_filedata_from_memfile(struct MemFile *memfile,
const struct BlendFileReadParams *params,
struct BlendFileReadReport *reports);
/**
* Lib linked proxy objects point to our local data, we need
* to clear that pointer before reading the undo memfile since
* the object might be removed, it is set again in reading
* if the local object still exists.
* This is only valid for local proxy objects though, linked ones should not be affected here.
*/
void blo_clear_proxy_pointers_from_lib(struct Main *oldmain);
void blo_make_packed_pointer_map(FileData *fd, struct Main *oldmain);
/**
* Set old main packed data to zero if it has been restored

View File

@ -349,7 +349,7 @@ static void do_version_scene_collection_convert(
LISTBASE_FOREACH (LinkData *, link, &sc->objects) {
Object *ob = link->data;
if (ob) {
BKE_collection_object_add(bmain, collection, ob);
BKE_collection_object_add_notest(bmain, collection, ob);
id_us_min(&ob->id);
}
}
@ -459,7 +459,7 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene)
/* Note usually this would do slow collection syncing for view layers,
* but since no view layers exists yet at this point it's fast. */
BKE_collection_object_add(bmain, collections[layer], base->object);
BKE_collection_object_add_notest(bmain, collections[layer], base->object);
}
if (base->flag & SELECT) {
@ -1235,7 +1235,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
(*collection_hidden)->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER;
}
BKE_collection_object_add(bmain, *collection_hidden, ob);
BKE_collection_object_add_notest(bmain, *collection_hidden, ob);
BKE_collection_object_remove(bmain, collection, ob, true);
}
}

View File

@ -1122,7 +1122,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Hair and PointCloud attributes. */
for (Hair *hair = bmain->hairs.first; hair != NULL; hair = hair->id.next) {
do_versions_point_attributes(&hair->pdata);
do_versions_point_attributes(&hair->geometry.point_data);
}
for (PointCloud *pointcloud = bmain->pointclouds.first; pointcloud != NULL;
pointcloud = pointcloud->id.next) {
@ -1424,7 +1424,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Hair and PointCloud attributes names. */
LISTBASE_FOREACH (Hair *, hair, &bmain->hairs) {
do_versions_point_attribute_names(&hair->pdata);
do_versions_point_attribute_names(&hair->geometry.point_data);
}
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
do_versions_point_attribute_names(&pointcloud->pdata);

View File

@ -974,6 +974,13 @@ void blo_do_versions_userdef(UserDef *userdef)
*/
{
/* Keep this block, even when empty. */
if (!USER_VERSION_ATLEAST(301, 7)) {
/* io_scene_obj directory is gone, split into io_import_obj and io_export_obj,
* with io_import_obj enabled by default and io_export_obj replaced by the C++ version.
*/
BKE_addon_remove_safe(&userdef->addons, "io_scene_obj");
BKE_addon_ensure(&userdef->addons, "io_import_obj");
}
}
LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) {

View File

@ -192,12 +192,12 @@ static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out, bool keep_hidden
BM_elem_flag_enable(bmv, KEEP_FLAG);
}
else {
new_bmvs[v] = NULL;
new_bmvs[v] = nullptr;
}
}
for (int v : m_out.vert_index_range()) {
const Vert *vertp = m_out.vert(v);
if (new_bmvs[v] == NULL) {
if (new_bmvs[v] == nullptr) {
float co[3];
const double3 &d_co = vertp->co;
for (int i = 0; i < 3; ++i) {

View File

@ -274,10 +274,14 @@ set(SRC
# converter nodes
nodes/COM_CombineColorNode.cc
nodes/COM_CombineColorNode.h
nodes/COM_CombineXYZNode.cc
nodes/COM_CombineXYZNode.h
nodes/COM_IDMaskNode.cc
nodes/COM_IDMaskNode.h
nodes/COM_SeparateColorNode.cc
nodes/COM_SeparateColorNode.h
nodes/COM_SeparateXYZNode.cc
nodes/COM_SeparateXYZNode.h
nodes/COM_MapRangeNode.cc
nodes/COM_MapRangeNode.h

View File

@ -44,6 +44,7 @@
#include "COM_ColorSpillNode.h"
#include "COM_ColorToBWNode.h"
#include "COM_CombineColorNode.h"
#include "COM_CombineXYZNode.h"
#include "COM_CompositorNode.h"
#include "COM_ConvertAlphaNode.h"
#include "COM_ConvertColorSpaceNode.h"
@ -96,6 +97,7 @@
#include "COM_ScaleOperation.h"
#include "COM_SceneTimeNode.h"
#include "COM_SeparateColorNode.h"
#include "COM_SeparateXYZNode.h"
#include "COM_SetAlphaNode.h"
#include "COM_SetValueOperation.h"
#include "COM_SplitViewerNode.h"
@ -434,6 +436,12 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_CONVERT_COLOR_SPACE:
node = new ConvertColorSpaceNode(b_node);
break;
case CMP_NODE_SEPARATE_XYZ:
node = new SeparateXYZNode(b_node);
break;
case CMP_NODE_COMBINE_XYZ:
node = new CombineXYZNode(b_node);
break;
}
return node;
}

View File

@ -0,0 +1,55 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#include "COM_CombineXYZNode.h"
#include "COM_ConvertOperation.h"
namespace blender::compositor {
CombineXYZNode::CombineXYZNode(bNode *editor_node) : Node(editor_node)
{
}
void CombineXYZNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &UNUSED(context)) const
{
NodeInput *input_x_socket = this->get_input_socket(0);
NodeInput *input_y_socket = this->get_input_socket(1);
NodeInput *input_z_socket = this->get_input_socket(2);
NodeOutput *output_socket = this->get_output_socket(0);
CombineChannelsOperation *operation = new CombineChannelsOperation();
if (input_x_socket->is_linked()) {
operation->set_canvas_input_index(0);
}
else if (input_y_socket->is_linked()) {
operation->set_canvas_input_index(1);
}
else {
operation->set_canvas_input_index(2);
}
converter.add_operation(operation);
converter.map_input_socket(input_x_socket, operation->get_input_socket(0));
converter.map_input_socket(input_y_socket, operation->get_input_socket(1));
converter.map_input_socket(input_z_socket, operation->get_input_socket(2));
converter.map_output_socket(output_socket, operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -0,0 +1,36 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#pragma once
#include "COM_Node.h"
namespace blender::compositor {
/**
* \brief SeparateXYZNode
* \ingroup Node
*/
class CombineXYZNode : public Node {
public:
CombineXYZNode(bNode *editor_node);
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
};
} // namespace blender::compositor

View File

@ -0,0 +1,63 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#include "COM_SeparateXYZNode.h"
#include "COM_ConvertOperation.h"
namespace blender::compositor {
SeparateXYZNode::SeparateXYZNode(bNode *editor_node) : Node(editor_node)
{
/* pass */
}
void SeparateXYZNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &UNUSED(context)) const
{
NodeInput *vector_socket = this->get_input_socket(0);
NodeOutput *output_x_socket = this->get_output_socket(0);
NodeOutput *output_y_socket = this->get_output_socket(1);
NodeOutput *output_z_socket = this->get_output_socket(2);
{
SeparateChannelOperation *operation = new SeparateChannelOperation();
operation->set_channel(0);
converter.add_operation(operation);
converter.map_input_socket(vector_socket, operation->get_input_socket(0));
converter.map_output_socket(output_x_socket, operation->get_output_socket(0));
}
{
SeparateChannelOperation *operation = new SeparateChannelOperation();
operation->set_channel(1);
converter.add_operation(operation);
converter.map_input_socket(vector_socket, operation->get_input_socket(0));
converter.map_output_socket(output_y_socket, operation->get_output_socket(0));
}
{
SeparateChannelOperation *operation = new SeparateChannelOperation();
operation->set_channel(2);
converter.add_operation(operation);
converter.map_input_socket(vector_socket, operation->get_input_socket(0));
converter.map_output_socket(output_z_socket, operation->get_output_socket(0));
}
}
} // namespace blender::compositor

View File

@ -0,0 +1,36 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#pragma once
#include "COM_Node.h"
namespace blender::compositor {
/**
* \brief SeparateXYZNode
* \ingroup Node
*/
class SeparateXYZNode : public Node {
public:
SeparateXYZNode(bNode *editor_node);
void convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const override;
};
} // namespace blender::compositor

View File

@ -111,9 +111,6 @@ typedef enum eDepsObjectComponentType {
/* Parameters Component - Default when nothing else fits
* (i.e. just SDNA property setting). */
DEG_OB_COMP_PARAMETERS,
/* Generic "Proxy-Inherit" Component.
* TODO(sergey): Also for instancing of subgraphs? */
DEG_OB_COMP_PROXY,
/* Animation Component.
*
* TODO(sergey): merge in with parameters? */

View File

@ -125,10 +125,6 @@ bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel
bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan)
{
/* Proxies don't have BONE_SEGMENTS */
if (ID_IS_LINKED(object) && object->proxy_from != nullptr) {
return false;
}
return check_pchan_has_bbone(object, pchan);
}

View File

@ -726,9 +726,6 @@ void DepsgraphNodeBuilder::build_object(int base_index,
eDepsNode_LinkedState_Type linked_state,
bool is_visible)
{
if (object->proxy != nullptr) {
object->proxy->proxy_from = object;
}
const bool has_object = built_map_.checkIsBuiltAndTag(object);
/* When there is already object in the dependency graph accumulate visibility an linked state
@ -819,9 +816,6 @@ void DepsgraphNodeBuilder::build_object(int base_index,
(object->pd->tex != nullptr)) {
build_texture(object->pd->tex);
}
/* Proxy object to copy from. */
build_object_proxy_from(object, is_visible);
build_object_proxy_group(object, is_visible);
/* Object dupligroup. */
if (object->instance_collection != nullptr) {
build_object_instance_collection(object, is_visible);
@ -875,22 +869,6 @@ void DepsgraphNodeBuilder::build_object_flags(int base_index,
});
}
void DepsgraphNodeBuilder::build_object_proxy_from(Object *object, bool is_object_visible)
{
if (object->proxy_from == nullptr) {
return;
}
build_object(-1, object->proxy_from, DEG_ID_LINKED_INDIRECTLY, is_object_visible);
}
void DepsgraphNodeBuilder::build_object_proxy_group(Object *object, bool is_object_visible)
{
if (object->proxy_group == nullptr) {
return;
}
build_object(-1, object->proxy_group, DEG_ID_LINKED_INDIRECTLY, is_object_visible);
}
void DepsgraphNodeBuilder::build_object_instance_collection(Object *object, bool is_object_visible)
{
if (object->instance_collection == nullptr) {
@ -922,12 +900,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object)
build_object_data_geometry(object);
break;
case OB_ARMATURE:
if (ID_IS_LINKED(object) && object->proxy_from != nullptr) {
build_proxy_rig(object);
}
else {
build_rig(object);
}
build_rig(object);
break;
case OB_LAMP:
build_object_data_light(object);
@ -1183,12 +1156,6 @@ void DepsgraphNodeBuilder::build_driver_variables(ID *id, FCurve *fcurve)
}
build_id(dtar->id);
build_driver_id_property(dtar->id, dtar->rna_path);
/* Corresponds to dtar_id_ensure_proxy_from(). */
if ((GS(dtar->id->name) == ID_OB) && (((Object *)dtar->id)->proxy_from != nullptr)) {
Object *proxy_from = ((Object *)dtar->id)->proxy_from;
build_id(&proxy_from->id);
build_driver_id_property(&proxy_from->id, dtar->rna_path);
}
}
DRIVER_TARGETS_LOOPER_END;
}

View File

@ -184,8 +184,6 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
Object *object,
eDepsNode_LinkedState_Type linked_state,
bool is_visible);
virtual void build_object_proxy_from(Object *object, bool is_object_visible);
virtual void build_object_proxy_group(Object *object, bool is_object_visible);
virtual void build_object_instance_collection(Object *object, bool is_object_visible);
virtual void build_object_from_layer(int base_index,
Object *object,
@ -232,7 +230,6 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);
virtual void build_splineik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);
virtual void build_rig(Object *object);
virtual void build_proxy_rig(Object *object);
virtual void build_armature(bArmature *armature);
virtual void build_armature_bones(ListBase *bones);
virtual void build_shapekeys(Key *key);

View File

@ -306,72 +306,4 @@ void DepsgraphNodeBuilder::build_rig(Object *object)
}
}
void DepsgraphNodeBuilder::build_proxy_rig(Object *object)
{
bArmature *armature = (bArmature *)object->data;
OperationNode *op_node;
Object *object_cow = get_cow_datablock(object);
/* Sanity check. */
BLI_assert(object->pose != nullptr);
/* Armature. */
build_armature(armature);
/* speed optimization for animation lookups */
BKE_pose_channels_hash_ensure(object->pose);
if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(object->pose);
}
op_node = add_operation_node(
&object->id,
NodeType::EVAL_POSE,
OperationCode::POSE_INIT,
[object_cow](::Depsgraph *depsgraph) { BKE_pose_eval_proxy_init(depsgraph, object_cow); });
op_node->set_as_entry();
int pchan_index = 0;
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
op_node = add_operation_node(
&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL);
op_node->set_as_entry();
/* Bone is ready for solvers. */
add_operation_node(&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_READY);
/* Bone is fully evaluated. */
op_node = add_operation_node(&object->id,
NodeType::BONE,
pchan->name,
OperationCode::BONE_DONE,
[object_cow, pchan_index](::Depsgraph *depsgraph) {
BKE_pose_eval_proxy_copy_bone(
depsgraph, object_cow, pchan_index);
});
op_node->set_as_exit();
/* Custom properties. */
if (pchan->prop != nullptr) {
build_idproperties(pchan->prop);
add_operation_node(
&object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan->name);
}
/* Custom shape. */
if (pchan->custom != nullptr) {
/* NOTE: The relation builder will ensure visibility of the custom shape object. */
build_object(-1, pchan->custom, DEG_ID_LINKED_INDIRECTLY, false);
}
pchan_index++;
}
op_node = add_operation_node(&object->id,
NodeType::EVAL_POSE,
OperationCode::POSE_CLEANUP,
[object_cow](::Depsgraph *depsgraph) {
BKE_pose_eval_proxy_cleanup(depsgraph, object_cow);
});
op_node = add_operation_node(
&object->id,
NodeType::EVAL_POSE,
OperationCode::POSE_DONE,
[object_cow](::Depsgraph *depsgraph) { BKE_pose_eval_proxy_done(depsgraph, object_cow); });
op_node->set_as_exit();
}
} // namespace blender::deg

View File

@ -787,9 +787,6 @@ void DepsgraphRelationBuilder::build_object(Object *object)
(object->pd->tex != nullptr)) {
build_texture(object->pd->tex);
}
/* Proxy object to copy from. */
build_object_proxy_from(object);
build_object_proxy_group(object);
/* Object dupligroup. */
if (object->instance_collection != nullptr) {
build_collection(nullptr, object, object->instance_collection);
@ -804,31 +801,6 @@ void DepsgraphRelationBuilder::build_object(Object *object)
build_parameters(&object->id);
}
void DepsgraphRelationBuilder::build_object_proxy_from(Object *object)
{
if (object->proxy_from == nullptr) {
return;
}
/* Object is linked here (comes from the library). */
build_object(object->proxy_from);
ComponentKey ob_transform_key(&object->proxy_from->id, NodeType::TRANSFORM);
ComponentKey proxy_transform_key(&object->id, NodeType::TRANSFORM);
add_relation(ob_transform_key, proxy_transform_key, "Proxy Transform");
}
void DepsgraphRelationBuilder::build_object_proxy_group(Object *object)
{
if (ELEM(object->proxy_group, nullptr, object->proxy)) {
return;
}
/* Object is local here (local in .blend file, users interacts with it). */
build_object(object->proxy_group);
OperationKey proxy_group_eval_key(
&object->proxy_group->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
OperationKey transform_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
add_relation(proxy_group_eval_key, transform_eval_key, "Proxy Group Transform");
}
void DepsgraphRelationBuilder::build_object_from_layer_relations(Object *object)
{
OperationKey object_from_layer_entry_key(
@ -895,12 +867,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object)
break;
}
case OB_ARMATURE:
if (ID_IS_LINKED(object) && object->proxy_from != nullptr) {
build_proxy_rig(object);
}
else {
build_rig(object);
}
build_rig(object);
break;
case OB_LAMP:
build_object_data_light(object);
@ -1663,18 +1630,9 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu)
}
build_id(target_id);
build_driver_id_property(target_id, dtar->rna_path);
/* Look up the proxy - matches dtar_id_ensure_proxy_from during evaluation. */
Object *object = nullptr;
if (GS(target_id->name) == ID_OB) {
object = (Object *)target_id;
if (object->proxy_from != nullptr) {
/* Redirect the target to the proxy, like in evaluation. */
object = object->proxy_from;
target_id = &object->id;
/* Prepare the redirected target. */
build_id(target_id);
build_driver_id_property(target_id, dtar->rna_path);
}
}
/* Special handling for directly-named bones. */
if ((dtar->flag & DTAR_FLAG_STRUCT_REF) && (object && object->type == OB_ARMATURE) &&

View File

@ -216,8 +216,6 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
Object *object,
Collection *collection);
virtual void build_object(Object *object);
virtual void build_object_proxy_from(Object *object);
virtual void build_object_proxy_group(Object *object);
virtual void build_object_from_layer_relations(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
@ -273,7 +271,6 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
const bPoseChannel *rootchan,
const RootPChanMap *root_map);
virtual void build_rig(Object *object);
virtual void build_proxy_rig(Object *object);
virtual void build_shapekeys(Key *key);
virtual void build_armature(bArmature *armature);
virtual void build_armature_bones(ListBase *bones);

View File

@ -471,65 +471,4 @@ void DepsgraphRelationBuilder::build_rig(Object *object)
}
}
void DepsgraphRelationBuilder::build_proxy_rig(Object *object)
{
bArmature *armature = (bArmature *)object->data;
Object *proxy_from = object->proxy_from;
build_armature(armature);
OperationKey pose_init_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_INIT);
OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE);
OperationKey pose_cleanup_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_CLEANUP);
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
build_idproperties(pchan->prop);
OperationKey bone_local_key(
&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL);
OperationKey bone_ready_key(
&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_READY);
OperationKey bone_done_key(&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_DONE);
OperationKey from_bone_done_key(
&proxy_from->id, NodeType::BONE, pchan->name, OperationCode::BONE_DONE);
add_relation(pose_init_key, bone_local_key, "Pose Init -> Bone Local");
add_relation(bone_local_key, bone_ready_key, "Local -> Ready");
add_relation(bone_ready_key, bone_done_key, "Ready -> Done");
add_relation(bone_done_key, pose_cleanup_key, "Bone Done -> Pose Cleanup");
add_relation(bone_done_key, pose_done_key, "Bone Done -> Pose Done", RELATION_FLAG_GODMODE);
/* Make sure bone in the proxy is not done before its FROM is done. */
if (check_pchan_has_bbone(object, pchan)) {
OperationKey from_bone_segments_key(
&proxy_from->id, NodeType::BONE, pchan->name, OperationCode::BONE_SEGMENTS);
add_relation(from_bone_segments_key,
bone_done_key,
"Bone Segments -> Bone Done",
RELATION_FLAG_GODMODE);
}
else {
add_relation(from_bone_done_key, bone_done_key, "Bone Done -> Bone Done");
}
/* Parent relation: even though the proxy bone itself doesn't need
* the parent bone, some users expect the parent to be ready if the
* bone itself is (e.g. for computing the local space matrix).
*/
if (pchan->parent != nullptr) {
OperationKey parent_key(
&object->id, NodeType::BONE, pchan->parent->name, OperationCode::BONE_DONE);
add_relation(parent_key, bone_done_key, "Parent Bone -> Child Bone");
}
if (pchan->prop != nullptr) {
OperationKey bone_parameters(
&object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, pchan->name);
OperationKey from_bone_parameters(
&proxy_from->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, pchan->name);
add_relation(from_bone_parameters, bone_parameters, "Proxy Bone Parameters");
}
/* Custom shape. */
if (pchan->custom != nullptr) {
build_object(pchan->custom);
add_visibility_relation(&pchan->custom->id, &armature->id);
}
}
}
} // namespace blender::deg

View File

@ -63,17 +63,6 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder {
return DepsgraphNodeBuilder::need_pull_base_into_graph(base);
}
void build_object_proxy_group(Object *object, bool is_visible) override
{
if (object->proxy_group == nullptr) {
return;
}
if (!filter_.contains(&object->proxy_group->id)) {
return;
}
DepsgraphNodeBuilder::build_object_proxy_group(object, is_visible);
}
protected:
DepsgraphFromIDsFilter filter_;
};
@ -96,17 +85,6 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder {
return DepsgraphRelationBuilder::need_pull_base_into_graph(base);
}
void build_object_proxy_group(Object *object) override
{
if (object->proxy_group == nullptr) {
return;
}
if (!filter_.contains(&object->proxy_group->id)) {
return;
}
DepsgraphRelationBuilder::build_object_proxy_group(object);
}
protected:
DepsgraphFromIDsFilter filter_;
};

View File

@ -36,10 +36,7 @@ namespace deg {
* visibility and other flags assigned to the objects.
* All other bases (the ones which points to object which is outside of the set of IDs) are
* completely ignored.
*
* - Proxy groups pointing to objects which are outside of the IDs set are also ignored.
* This way we avoid high-poly character body pulled into the dependency graph when it's coming
* from a library into an animation file and the dependency graph constructed for a proxy rig. */
*/
class FromIDsBuilderPipeline : public AbstractBuilderPipeline {
public:

View File

@ -99,7 +99,6 @@ static const int deg_debug_node_type_color_map[][2] = {
/* Outer Types */
{NodeType::PARAMETERS, 2},
{NodeType::PROXY, 3},
{NodeType::ANIMATION, 4},
{NodeType::TRANSFORM, 5},
{NodeType::GEOMETRY, 6},
@ -404,7 +403,6 @@ static void deg_debug_graphviz_node(DotExportContext &ctx,
case NodeType::PARAMETERS:
case NodeType::ANIMATION:
case NodeType::TRANSFORM:
case NodeType::PROXY:
case NodeType::GEOMETRY:
case NodeType::SEQUENCER:
case NodeType::EVAL_POSE:

View File

@ -72,6 +72,14 @@ void DEG_evaluate_on_refresh(Depsgraph *graph)
deg_graph->frame = frame;
deg_graph->ctime = ctime;
}
else if (scene->id.recalc & ID_RECALC_FRAME_CHANGE) {
/* Comparing depsgraph & scene frame fails in the case of undo,
* since the undo state is stored before updates from the frame change have been applied.
* In this case reading back the undo state will behave as if no updates on frame change
* is needed as the #Depsgraph.ctime & frame will match the values in the input scene.
* Use #ID_RECALC_FRAME_CHANGE to detect that recalculation is necessary. see: T66913. */
deg_graph->tag_time_source();
}
deg_flush_updates_and_refresh(deg_graph);
}

View File

@ -211,7 +211,7 @@ void depsgraph_tag_to_component_opcode(const ID *id,
case ID_RECALC_SEQUENCER_STRIPS:
*component_type = NodeType::SEQUENCER;
break;
case ID_RECALC_AUDIO_SEEK:
case ID_RECALC_FRAME_CHANGE:
case ID_RECALC_AUDIO_FPS:
case ID_RECALC_AUDIO_VOLUME:
case ID_RECALC_AUDIO_MUTE:
@ -284,6 +284,7 @@ void depsgraph_tag_component(Depsgraph *graph,
* here. */
if (component_node == nullptr) {
if (component_type == NodeType::ANIMATION) {
id_node->is_cow_explicitly_tagged = true;
depsgraph_id_tag_copy_on_write(graph, id_node, update_source);
}
return;
@ -301,6 +302,9 @@ void depsgraph_tag_component(Depsgraph *graph,
if (component_node->need_tag_cow_before_update()) {
depsgraph_id_tag_copy_on_write(graph, id_node, update_source);
}
if (component_type == NodeType::COPY_ON_WRITE) {
id_node->is_cow_explicitly_tagged = true;
}
}
/* This is a tag compatibility with legacy code.
@ -522,12 +526,6 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph)
* this. */
for (deg::IDNode *id_node : graph->id_nodes) {
const ID_Type id_type = GS(id_node->id_orig->name);
if (id_type == ID_OB) {
Object *object_orig = reinterpret_cast<Object *>(id_node->id_orig);
if (object_orig->proxy != nullptr) {
object_orig->proxy->proxy_from = object_orig;
}
}
if (!id_node->visible_components_mask) {
/* ID has no components which affects anything visible.
@ -733,8 +731,8 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag)
return "EDITORS";
case ID_RECALC_SEQUENCER_STRIPS:
return "SEQUENCER_STRIPS";
case ID_RECALC_AUDIO_SEEK:
return "AUDIO_SEEK";
case ID_RECALC_FRAME_CHANGE:
return "FRAME_CHANGE";
case ID_RECALC_AUDIO_FPS:
return "AUDIO_FPS";
case ID_RECALC_AUDIO_VOLUME:
@ -888,6 +886,7 @@ void DEG_ids_clear_recalc(Depsgraph *depsgraph, const bool backup)
* correctly when there are multiple depsgraph with others still using
* the recalc flag. */
id_node->is_user_modified = false;
id_node->is_cow_explicitly_tagged = false;
deg_graph_clear_id_recalc_flags(id_node->id_cow);
if (deg_graph->is_active) {
deg_graph_clear_id_recalc_flags(id_node->id_orig);

View File

@ -674,12 +674,6 @@ void update_pose_orig_pointers(const bPose *pose_orig, bPose *pose_cow)
update_list_orig_pointers(&pose_orig->chanbase, &pose_cow->chanbase, &bPoseChannel::orig_pchan);
}
void update_modifiers_orig_pointers(const Object *object_orig, Object *object_cow)
{
update_list_orig_pointers(
&object_orig->modifiers, &object_cow->modifiers, &ModifierData::orig_modifier_data);
}
void update_nla_strips_orig_pointers(const ListBase *strips_orig, ListBase *strips_cow)
{
NlaStrip *strip_orig = reinterpret_cast<NlaStrip *>(strips_orig->first);
@ -714,25 +708,6 @@ void update_animation_data_after_copy(const ID *id_orig, ID *id_cow)
update_nla_tracks_orig_pointers(&anim_data_orig->nla_tracks, &anim_data_cow->nla_tracks);
}
/* Some builders (like motion path one) will ignore proxies from being built. This code makes it so
* proxy and proxy_group pointers never point to an original objects, preventing evaluation code
* from assign evaluated pointer to an original proxy->proxy_from. */
void update_proxy_pointers_after_copy(const Depsgraph *depsgraph,
const Object *object_orig,
Object *object_cow)
{
if (object_cow->proxy != nullptr) {
if (!deg_check_id_in_depsgraph(depsgraph, &object_orig->proxy->id)) {
object_cow->proxy = nullptr;
}
}
if (object_cow->proxy_group != nullptr) {
if (!deg_check_id_in_depsgraph(depsgraph, &object_orig->proxy_group->id)) {
object_cow->proxy_group = nullptr;
}
}
}
/* Do some special treatment of data transfer from original ID to its
* CoW complementary part.
*
@ -766,8 +741,6 @@ void update_id_after_copy(const Depsgraph *depsgraph,
BKE_gpencil_update_orig_pointers(object_orig, object_cow);
}
update_particles_after_copy(depsgraph, object_orig, object_cow);
update_modifiers_orig_pointers(object_orig, object_cow);
update_proxy_pointers_after_copy(depsgraph, object_orig, object_cow);
break;
}
case ID_SCE: {
@ -904,7 +877,7 @@ ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode
* modifiers.
*
* TODO: Investigate modes besides edit-mode. */
if (check_datablock_expanded(id_cow)) {
if (check_datablock_expanded(id_cow) && !id_node->is_cow_explicitly_tagged) {
const ID_Type id_type = GS(id_orig->name);
if (OB_DATA_SUPPORT_EDITMODE(id_type) && BKE_object_data_is_in_editmode(id_orig)) {
/* Make sure pointers in the edit mode data are updated in the copy.

View File

@ -71,7 +71,6 @@ void ObjectRuntimeBackup::backup_modifier_runtime_data(Object *object)
const SessionUUID &session_uuid = modifier_data->session_uuid;
BLI_assert(BLI_session_uuid_is_generated(&session_uuid));
BLI_assert(modifier_data->orig_modifier_data != nullptr);
modifier_runtime_data.add(session_uuid, ModifierDataBackup(modifier_data));
modifier_data->runtime = nullptr;
}
@ -150,8 +149,9 @@ void ObjectRuntimeBackup::restore_to_object(Object *object)
void ObjectRuntimeBackup::restore_modifier_runtime_data(Object *object)
{
LISTBASE_FOREACH (ModifierData *, modifier_data, &object->modifiers) {
BLI_assert(modifier_data->orig_modifier_data != nullptr);
const SessionUUID &session_uuid = modifier_data->session_uuid;
BLI_assert(BLI_session_uuid_is_generated(&session_uuid));
optional<ModifierDataBackup> backup = modifier_runtime_data.pop_try(session_uuid);
if (backup.has_value()) {
modifier_data->runtime = backup->runtime;

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