WIP: Brush assets project #106303

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

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
15 changed files with 202 additions and 149 deletions
Showing only changes of commit 62715f4230 - Show all commits

View File

@ -30,7 +30,7 @@ but some areas are still being extended and improved.
Before Starting
===============
This document its intended to familiarize you with Blender Python API
This document is intended to familiarize you with Blender Python API
but not to fully cover each topic.
A quick list of helpful things to know before starting:

View File

@ -406,7 +406,12 @@ def draw_keymaps(context, layout):
rowsubsub = rowsub.row(align=True)
if not ok:
rowsubsub.alert = True
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM')
search_placeholder = ""
if spref.filter_type == 'NAME':
search_placeholder = iface_("Search by Name")
elif spref.filter_type == 'KEY':
search_placeholder = iface_("Search by Key-Binding")
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM', placeholder=search_placeholder)
if not filter_text:
# When the keyconfig defines its own preferences.

View File

@ -435,6 +435,7 @@ void BKE_crazyspace_build_sculpt(Depsgraph *depsgraph,
VirtualModifierData virtual_modifier_data;
Object object_eval;
crazyspace_init_object_for_eval(depsgraph, object, &object_eval);
BLI_SCOPED_DEFER([&]() { MEM_delete(object_eval.runtime); });
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(&object_eval,
&virtual_modifier_data);
const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)};

View File

@ -2233,91 +2233,97 @@ bool GreasePencil::remove_frames(blender::bke::greasepencil::Layer &layer,
return false;
}
static void remove_drawings_unchecked(GreasePencil &grease_pencil,
Span<int64_t> sorted_indices_to_remove)
{
using namespace blender::bke::greasepencil;
if (grease_pencil.drawing_array_num == 0 || sorted_indices_to_remove.is_empty()) {
return;
}
const int64_t drawings_to_remove = sorted_indices_to_remove.size();
const blender::IndexRange last_drawings_range(
grease_pencil.drawings().size() - drawings_to_remove, drawings_to_remove);
/* We keep track of the next available index (for swapping) by iterating from the end and
* skipping over drawings that are already in the range to be removed. */
auto next_available_index = last_drawings_range.last();
auto greatest_index_to_remove_it = std::rbegin(sorted_indices_to_remove);
auto get_next_available_index = [&]() {
while (next_available_index == *greatest_index_to_remove_it) {
greatest_index_to_remove_it = std::prev(greatest_index_to_remove_it);
next_available_index--;
}
return next_available_index;
};
/* Move the drawings to be removed to the end of the array by swapping the pointers. Make sure to
* remap any frames pointing to the drawings being swapped. */
for (const int64_t index_to_remove : sorted_indices_to_remove) {
if (index_to_remove >= last_drawings_range.first()) {
/* This drawing and all the next drawings are already in the range to be removed. */
break;
}
const int64_t swap_index = get_next_available_index();
/* Remap the drawing_index for frames that point to the drawing to be swapped with. */
for (Layer *layer : grease_pencil.layers_for_write()) {
for (auto [key, value] : layer->frames_for_write().items()) {
if (value.drawing_index == swap_index) {
value.drawing_index = index_to_remove;
layer->tag_frames_map_changed();
}
}
}
/* Swap the pointers to the drawings in the drawing array. */
std::swap(grease_pencil.drawing_array[index_to_remove],
grease_pencil.drawing_array[swap_index]);
next_available_index--;
}
/* Free the last drawings. */
for (const int64_t drawing_index : last_drawings_range) {
GreasePencilDrawingBase *drawing_base_to_remove = grease_pencil.drawing(drawing_index);
switch (drawing_base_to_remove->type) {
case GP_DRAWING: {
GreasePencilDrawing *drawing_to_remove = reinterpret_cast<GreasePencilDrawing *>(
drawing_base_to_remove);
MEM_delete(&drawing_to_remove->wrap());
break;
}
case GP_DRAWING_REFERENCE: {
GreasePencilDrawingReference *drawing_reference_to_remove =
reinterpret_cast<GreasePencilDrawingReference *>(drawing_base_to_remove);
MEM_delete(&drawing_reference_to_remove->wrap());
break;
}
}
}
/* Shrink drawing array. */
shrink_array<GreasePencilDrawingBase *>(
&grease_pencil.drawing_array, &grease_pencil.drawing_array_num, drawings_to_remove);
}
void GreasePencil::remove_drawings_with_no_users()
{
using namespace blender;
Vector<int64_t> drawings_to_be_removed;
for (const int64_t drawing_i : this->drawings().index_range()) {
GreasePencilDrawingBase *drawing_base = this->drawing(drawing_i);
using namespace blender::bke::greasepencil;
/* Compress the drawings array by finding unused drawings.
* In every step two indices are found:
* - The next unused drawing from the start
* - The last used drawing from the end
* These two drawings are then swapped. Rinse and repeat until both iterators meet somewhere in
* the middle. At this point the drawings array is fully compressed.
* Then the drawing indices in frame data are remapped. */
const MutableSpan<GreasePencilDrawingBase *> drawings = this->drawings();
if (drawings.is_empty()) {
return;
}
auto is_drawing_used = [&](const int drawing_index) {
GreasePencilDrawingBase *drawing_base = drawings[drawing_index];
/* Note: GreasePencilDrawingReference does not have a user count currently, but should
* eventually be counted like GreasePencilDrawing. */
if (drawing_base->type != GP_DRAWING) {
continue;
return false;
}
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
if (!drawing->wrap().has_users()) {
drawings_to_be_removed.append(drawing_i);
return drawing->wrap().has_users();
};
/* Index map to remap drawing indices in frame data.
* Index -1 indicates that the drawing has not been moved. */
constexpr const int unchanged_index = -1;
Array<int> drawing_index_map(drawings.size(), unchanged_index);
int first_unused_drawing = -1;
int last_used_drawing = drawings.size();
/* Advance head and tail iterators to the next unused/used drawing respectively.
* Returns true if an index pair was found that needs to be swapped. */
auto find_next_swap_index = [&]() -> bool {
do {
++first_unused_drawing;
} while (first_unused_drawing < last_used_drawing && is_drawing_used(first_unused_drawing));
do {
--last_used_drawing;
} while (first_unused_drawing < last_used_drawing && !is_drawing_used(last_used_drawing));
return first_unused_drawing < last_used_drawing;
};
while (find_next_swap_index()) {
/* Found two valid iterators, now swap drawings. */
std::swap(drawings[first_unused_drawing], drawings[last_used_drawing]);
drawing_index_map[last_used_drawing] = first_unused_drawing;
}
/* Tail range of unused drawings that can be removed. */
const IndexRange drawings_to_remove = drawings.index_range().drop_front(last_used_drawing + 1);
if (drawings_to_remove.is_empty()) {
return;
}
/* Free the unused drawings. */
for (const int i : drawings_to_remove) {
GreasePencilDrawingBase *unused_drawing_base = drawings[i];
switch (unused_drawing_base->type) {
case GP_DRAWING: {
auto *unused_drawing = reinterpret_cast<GreasePencilDrawing *>(unused_drawing_base);
MEM_delete(&unused_drawing->wrap());
break;
}
case GP_DRAWING_REFERENCE: {
auto *unused_drawing_ref = reinterpret_cast<GreasePencilDrawingReference *>(
unused_drawing_base);
MEM_delete(&unused_drawing_ref->wrap());
break;
}
}
}
shrink_array<GreasePencilDrawingBase *>(
&this->drawing_array, &this->drawing_array_num, drawings_to_remove.size());
/* Remap drawing indices in frame data. */
for (Layer *layer : this->layers_for_write()) {
for (auto [key, value] : layer->frames_for_write().items()) {
const int new_drawing_index = drawing_index_map[value.drawing_index];
if (new_drawing_index != unchanged_index) {
value.drawing_index = new_drawing_index;
layer->tag_frames_map_changed();
}
}
}
remove_drawings_unchecked(*this, drawings_to_be_removed.as_span());
}
void GreasePencil::update_drawing_users_for_layer(const blender::bke::greasepencil::Layer &layer)

View File

@ -929,27 +929,26 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
return mesh_in_bmain;
}
/* Make sure mesh only points original data-blocks, also increase users of materials and other
* possibly referenced data-blocks.
/* Make sure mesh only points to original data-blocks. Also increase user count of materials and
* other possibly referenced data-blocks.
*
* Going to original data-blocks is required to have bmain in a consistent state, where
* Changing to original data-blocks is required to have bmain in a consistent state, where
* everything is only allowed to reference original data-blocks.
*
* Note that user-count updates has to be done *after* mesh has been transferred to Main database
* (since doing reference-counting on non-Main IDs is forbidden). */
* Note that user-count updates have to be done *after* the mesh has been transferred to Main
* database (since doing reference-counting on non-Main IDs is forbidden). */
BKE_library_foreach_ID_link(
nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP);
/* Append the mesh to 'bmain'.
* We do it a bit longer way since there is no simple and clear way of adding existing data-block
* to the 'bmain'. So we allocate new empty mesh in the 'bmain' (which guarantees all the naming
* and orders and flags) and move the temporary mesh in place there. */
/* Add the mesh to 'bmain'. We do it in a bit longer way since there is no simple and clear way
* of adding existing data-blocks to the 'bmain'. So we create new empty mesh (which guarantees
* all the naming and order and flags) and move the temporary mesh in place there. */
Mesh *mesh_in_bmain = BKE_mesh_add(bmain, mesh->id.name + 2);
/* NOTE: BKE_mesh_nomain_to_mesh() does not copy materials and instead it preserves them in the
/* NOTE: BKE_mesh_nomain_to_mesh() does not copy materials and instead preserves them in the
* destination mesh. So we "steal" materials before calling it.
*
* TODO(sergey): We really better have a function which gets and ID and accepts it for the bmain.
* TODO(sergey): We really ought to have a function which gets an ID and accepts it into #Main.
*/
mesh_in_bmain->mat = mesh->mat;
mesh_in_bmain->totcol = mesh->totcol;

View File

@ -408,8 +408,8 @@ template<typename T>
/**
* \brief Create an orthographic projection matrix using OpenGL coordinate convention:
* Maps each axis range to [-1..1] range for all axes except Z.
* The Z axis is collapsed to 0 which eliminates the depth component. So it cannot be used with
* depth testing.
* The Z axis is almost collapsed to 0 which eliminates the depth component.
* So it should not be used with depth testing.
* The resulting matrix can be used with either #project_point or #transform_point.
*/
template<typename T> MatBase<T, 4, 4> orthographic_infinite(T left, T right, T bottom, T top);
@ -1581,7 +1581,8 @@ MatBase<T, 4, 4> orthographic(T left, T right, T bottom, T top, T near_clip, T f
return mat;
}
template<typename T> MatBase<T, 4, 4> orthographic_infinite(T left, T right, T bottom, T top)
template<typename T>
MatBase<T, 4, 4> orthographic_infinite(T left, T right, T bottom, T top, T near_clip)
{
const T x_delta = right - left;
const T y_delta = top - bottom;
@ -1592,8 +1593,13 @@ template<typename T> MatBase<T, 4, 4> orthographic_infinite(T left, T right, T b
mat[3][0] = -(right + left) / x_delta;
mat[1][1] = T(2.0) / y_delta;
mat[3][1] = -(top + bottom) / y_delta;
mat[2][2] = 0.0f;
mat[3][2] = 0.0f;
/* Page 17. Choosing an epsilon for 32 bit floating-point precision. */
constexpr float eps = 2.4e-7f;
/* From "Projection Matrix Tricks" by Eric Lengyel GDC 2007.
* Following same procedure as the reference but for orthographic matrix.
* This avoids degenerate matrix (0 determinant). */
mat[2][2] = -eps;
mat[3][2] = -1.0f - eps * near_clip;
}
return mat;
}

View File

@ -406,6 +406,12 @@ void DepsgraphNodeBuilder::begin_build()
saved_entry_tags_.append_as(op_node);
}
for (const OperationNode *op_node : graph_->operations) {
if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
needs_update_operations_.append_as(op_node);
}
}
/* Make sure graph has no nodes left from previous state. */
graph_->clear_all_nodes();
graph_->operations.clear();
@ -537,6 +543,16 @@ void DepsgraphNodeBuilder::tag_previously_tagged_nodes()
* that originally node was explicitly tagged for user update. */
operation_node->tag_update(graph_, DEG_UPDATE_SOURCE_USER_EDIT);
}
/* Restore needs-update flags since the previous state of the dependency graph, ensuring the
* previously-skipped operations are properly re-evaluated when needed. */
for (const OperationKey &operation_key : needs_update_operations_) {
OperationNode *operation_node = find_operation_node(operation_key);
if (operation_node == nullptr) {
continue;
}
operation_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
}
}
void DepsgraphNodeBuilder::end_build()

View File

@ -291,9 +291,14 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
};
protected:
/* Entry tags from the previous state of the dependency graph.
/* Entry tags and non-updated operations from the previous state of the dependency graph.
* The entry tags are operations which were directly tagged, the matching operations from the
* new dependency graph will be tagged. The needs-update operations are possibly indirectly
* modified operations, whose complementary part from the new dependency graph will only be
* marked as needs-update.
* Stored before the graph is re-created so that they can be transferred over. */
Vector<PersistentOperationKey> saved_entry_tags_;
Vector<PersistentOperationKey> needs_update_operations_;
struct BuilderWalkUserData {
DepsgraphNodeBuilder *builder;

View File

@ -19,7 +19,6 @@
#include "eevee_pipeline.hh"
#include "eevee_volume.hh"
#include <iostream>
namespace blender::eevee {
@ -333,7 +332,7 @@ void VolumeModule::draw_prepass(View &main_view)
* way, surfaces that are further away than the far clip plane will still be voxelized.*/
winmat_infinite = main_view.is_persp() ?
math::projection::perspective_infinite(left, right, bottom, top, near) :
math::projection::orthographic_infinite(left, right, bottom, top);
math::projection::orthographic_infinite(left, right, bottom, top, near);
/* We still need a bounded projection matrix to get correct froxel location. */
winmat_finite = main_view.is_persp() ?
math::projection::perspective(left, right, bottom, top, near, far) :

View File

@ -111,18 +111,36 @@ static void extract_normals_mesh(const MeshRenderData &mr, MutableSpan<GPUType>
template<typename GPUType>
static void extract_paint_overlay_flags(const MeshRenderData &mr, MutableSpan<GPUType> normals)
{
if (mr.select_poly.is_empty() && mr.hide_poly.is_empty() && (!mr.edit_bmesh || !mr.v_origindex))
{
const bool use_face_select = (mr.mesh->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
Span<bool> selection;
if (mr.mesh->editflag & ME_EDIT_PAINT_FACE_SEL) {
selection = mr.select_poly;
}
else if (mr.mesh->editflag & ME_EDIT_PAINT_VERT_SEL) {
selection = mr.select_vert;
}
if (selection.is_empty() && mr.hide_poly.is_empty() && (!mr.edit_bmesh || !mr.v_origindex)) {
return;
}
const OffsetIndices faces = mr.faces;
threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
if (!mr.select_poly.is_empty()) {
const Span<bool> select_poly = mr.select_poly;
for (const int face : range) {
if (select_poly[face]) {
if (!selection.is_empty()) {
if (use_face_select) {
for (const int face : range) {
if (selection[face]) {
for (const int corner : faces[face]) {
normals[corner].w = 1;
}
}
}
}
else {
const Span<int> corner_verts = mr.corner_verts;
for (const int face : range) {
for (const int corner : faces[face]) {
normals[corner].w = 1;
if (selection[corner_verts[corner]]) {
normals[corner].w = 1;
}
}
}
}

View File

@ -129,6 +129,8 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
params.global_scale = RNA_float_get(op->ptr, "global_scale");
RNA_string_get(op->ptr, "collection", params.collection);
/* Take some defaults from the scene, if not specified explicitly. */
Scene *scene = CTX_data_scene(C);
if (params.frame_start == INT_MIN) {
@ -144,7 +146,7 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
static void ui_alembic_export_settings(const bContext *C, uiLayout *layout, PointerRNA *imfptr)
{
uiLayout *box, *row, *col, *sub;
@ -185,9 +187,12 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
IFACE_("Custom Properties"),
ICON_NONE);
sub = uiLayoutColumnWithHeading(col, true, IFACE_("Only"));
uiItemR(sub, imfptr, "selected", UI_ITEM_NONE, IFACE_("Selected Objects"), ICON_NONE);
uiItemR(sub, imfptr, "visible_objects_only", UI_ITEM_NONE, IFACE_("Visible Objects"), ICON_NONE);
if (CTX_wm_space_file(C)) {
sub = uiLayoutColumnWithHeading(col, true, IFACE_("Only"));
uiItemR(sub, imfptr, "selected", UI_ITEM_NONE, IFACE_("Selected Objects"), ICON_NONE);
uiItemR(
sub, imfptr, "visible_objects_only", UI_ITEM_NONE, IFACE_("Visible Objects"), ICON_NONE);
}
col = uiLayoutColumn(box, true);
uiItemR(col, imfptr, "evaluation_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
@ -247,7 +252,7 @@ static void wm_alembic_export_draw(bContext *C, wmOperator *op)
RNA_boolean_set(op->ptr, "init_scene_frame_range", false);
}
ui_alembic_export_settings(op->layout, op->ptr);
ui_alembic_export_settings(C, op->layout, op->ptr);
}
static bool wm_alembic_export_check(bContext * /*C*/, wmOperator *op)
@ -365,6 +370,9 @@ void WM_OT_alembic_export(wmOperatorType *ot)
"Flatten Hierarchy",
"Do not preserve objects' parent/children relationship");
prop = RNA_def_string(ot->srna, "collection", nullptr, MAX_IDPROP_NAME, "Collection", nullptr);
RNA_def_property_flag(prop, PROP_HIDDEN);
RNA_def_boolean(ot->srna, "uvs", true, "UVs", "Export UVs");
RNA_def_boolean(ot->srna, "packuv", true, "Pack UV Islands", "Export UVs with packed island");
@ -724,6 +732,7 @@ void alembic_file_handler_add()
auto fh = std::make_unique<blender::bke::FileHandlerType>();
STRNCPY(fh->idname, "IO_FH_alembic");
STRNCPY(fh->import_operator, "WM_OT_alembic_import");
STRNCPY(fh->export_operator, "WM_OT_alembic_export");
STRNCPY(fh->label, "Alembic");
STRNCPY(fh->file_extensions_str, ".abc");
fh->poll_drop = poll_file_object_drop;

View File

@ -225,9 +225,7 @@ static const char *node_socket_get_translation_context(const bNodeSocket &socket
return translation_context.data();
}
static void node_socket_add_tooltip_in_node_editor(const bNodeTree &ntree,
const bNodeSocket &sock,
uiLayout &layout);
static void node_socket_add_tooltip_in_node_editor(const bNodeSocket &sock, uiLayout &layout);
/** Return true when \a a should be behind \a b and false otherwise. */
static bool compare_node_depth(const bNode *a, const bNode *b)
@ -520,12 +518,12 @@ static bool node_update_basis_socket(const bContext &C,
}
if (input_socket) {
node_socket_add_tooltip_in_node_editor(ntree, *input_socket, *row);
node_socket_add_tooltip_in_node_editor(*input_socket, *row);
/* Round the socket location to stop it from jiggling. */
input_socket->runtime->location = float2(round(locx), round(locy - NODE_DYS));
}
if (output_socket) {
node_socket_add_tooltip_in_node_editor(ntree, *output_socket, *row);
node_socket_add_tooltip_in_node_editor(*output_socket, *row);
/* Round the socket location to stop it from jiggling. */
output_socket->runtime->location = float2(round(locx + NODE_WIDTH(node)),
round(locy - NODE_DYS));
@ -1579,20 +1577,6 @@ static std::optional<std::string> create_socket_inspection_string(
return str;
}
static bool node_socket_has_tooltip(const bNodeTree &ntree, const bNodeSocket &socket)
{
if (ntree.type == NTREE_GEOMETRY) {
return true;
}
if (socket.runtime->declaration != nullptr) {
const nodes::SocketDeclaration &socket_decl = *socket.runtime->declaration;
return !socket_decl.description.empty();
}
return false;
}
static std::string node_socket_get_tooltip(const SpaceNode *snode,
const bNodeTree &ntree,
const bNodeSocket &socket)
@ -1647,13 +1631,8 @@ static std::string node_socket_get_tooltip(const SpaceNode *snode,
return output.str();
}
static void node_socket_add_tooltip_in_node_editor(const bNodeTree &ntree,
const bNodeSocket &sock,
uiLayout &layout)
static void node_socket_add_tooltip_in_node_editor(const bNodeSocket &sock, uiLayout &layout)
{
if (!node_socket_has_tooltip(ntree, sock)) {
return;
}
uiLayoutSetTooltipFunc(
&layout,
[](bContext *C, void *argN, const char * /*tip*/) {
@ -1670,10 +1649,6 @@ static void node_socket_add_tooltip_in_node_editor(const bNodeTree &ntree,
void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, uiLayout &layout)
{
if (!node_socket_has_tooltip(ntree, sock)) {
return;
}
struct SocketTooltipData {
const bNodeTree *ntree;
const bNodeSocket *socket;
@ -1727,10 +1702,6 @@ static void node_socket_draw_nested(const bContext &C,
size_id,
outline_col_id);
if (!node_socket_has_tooltip(ntree, sock)) {
return;
}
/* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible
* button on top of them for the tooltip. */
const eUIEmbossType old_emboss = UI_block_emboss_get(&block);

View File

@ -60,6 +60,8 @@ struct AlembicExportParams {
int ngon_method;
float global_scale;
char collection[MAX_IDPROP_NAME] = "";
};
struct AlembicImportParams {

View File

@ -17,6 +17,7 @@
#include "BKE_context.hh"
#include "BKE_global.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_scene.hh"
@ -49,14 +50,27 @@ struct ExportJobData {
namespace blender::io::alembic {
/* Construct the depsgraph for exporting. */
static void build_depsgraph(Depsgraph *depsgraph, const bool visible_objects_only)
static bool build_depsgraph(ExportJobData *job)
{
if (visible_objects_only) {
DEG_graph_build_from_view_layer(depsgraph);
if (strlen(job->params.collection) > 0) {
Collection *collection = reinterpret_cast<Collection *>(
BKE_libblock_find_name(job->bmain, ID_GR, job->params.collection));
if (!collection) {
WM_reportf(
RPT_ERROR, "Alembic Export: Unable to find collection '%s'", job->params.collection);
return false;
}
DEG_graph_build_from_collection(job->depsgraph, collection);
}
else if (job->params.visible_objects_only) {
DEG_graph_build_from_view_layer(job->depsgraph);
}
else {
DEG_graph_build_for_all_objects(depsgraph);
DEG_graph_build_for_all_objects(job->depsgraph);
}
return true;
}
static void report_job_duration(const ExportJobData *data)
@ -208,7 +222,9 @@ bool ABC_export(Scene *scene,
*
* Has to be done from main thread currently, as it may affect Main original data (e.g. when
* doing deferred update of the view-layers, see #112534 for details). */
blender::io::alembic::build_depsgraph(job->depsgraph, job->params.visible_objects_only);
if (!blender::io::alembic::build_depsgraph(job)) {
return false;
}
bool export_ok = false;
if (as_background_job) {

View File

@ -322,7 +322,7 @@ static Sequence *sequencer_check_scene_recursion(Scene *scene, ListBase *seqbase
}
if (seq->type == SEQ_TYPE_SCENE && (seq->flag & SEQ_SCENE_STRIPS)) {
if (sequencer_check_scene_recursion(scene, &seq->scene->ed->seqbase)) {
if (seq->scene->ed && sequencer_check_scene_recursion(scene, &seq->scene->ed->seqbase)) {
return seq;
}
}