WIP: Brush assets project #106303
|
@ -30,7 +30,7 @@ but some areas are still being extended and improved.
|
||||||
Before Starting
|
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.
|
but not to fully cover each topic.
|
||||||
|
|
||||||
A quick list of helpful things to know before starting:
|
A quick list of helpful things to know before starting:
|
||||||
|
|
|
@ -406,7 +406,12 @@ def draw_keymaps(context, layout):
|
||||||
rowsubsub = rowsub.row(align=True)
|
rowsubsub = rowsub.row(align=True)
|
||||||
if not ok:
|
if not ok:
|
||||||
rowsubsub.alert = True
|
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:
|
if not filter_text:
|
||||||
# When the keyconfig defines its own preferences.
|
# When the keyconfig defines its own preferences.
|
||||||
|
|
|
@ -435,6 +435,7 @@ void BKE_crazyspace_build_sculpt(Depsgraph *depsgraph,
|
||||||
VirtualModifierData virtual_modifier_data;
|
VirtualModifierData virtual_modifier_data;
|
||||||
Object object_eval;
|
Object object_eval;
|
||||||
crazyspace_init_object_for_eval(depsgraph, 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,
|
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(&object_eval,
|
||||||
&virtual_modifier_data);
|
&virtual_modifier_data);
|
||||||
const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)};
|
const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)};
|
||||||
|
|
|
@ -2233,91 +2233,97 @@ bool GreasePencil::remove_frames(blender::bke::greasepencil::Layer &layer,
|
||||||
return false;
|
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()
|
void GreasePencil::remove_drawings_with_no_users()
|
||||||
{
|
{
|
||||||
using namespace blender;
|
using namespace blender;
|
||||||
Vector<int64_t> drawings_to_be_removed;
|
using namespace blender::bke::greasepencil;
|
||||||
for (const int64_t drawing_i : this->drawings().index_range()) {
|
|
||||||
GreasePencilDrawingBase *drawing_base = this->drawing(drawing_i);
|
/* 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) {
|
if (drawing_base->type != GP_DRAWING) {
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
|
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
|
||||||
if (!drawing->wrap().has_users()) {
|
return drawing->wrap().has_users();
|
||||||
drawings_to_be_removed.append(drawing_i);
|
};
|
||||||
|
|
||||||
|
/* 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)
|
void GreasePencil::update_drawing_users_for_layer(const blender::bke::greasepencil::Layer &layer)
|
||||||
|
|
|
@ -929,27 +929,26 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
|
||||||
return mesh_in_bmain;
|
return mesh_in_bmain;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure mesh only points original data-blocks, also increase users of materials and other
|
/* Make sure mesh only points to original data-blocks. Also increase user count of materials and
|
||||||
* possibly referenced data-blocks.
|
* 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.
|
* 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
|
* Note that user-count updates have to be done *after* the mesh has been transferred to Main
|
||||||
* (since doing reference-counting on non-Main IDs is forbidden). */
|
* database (since doing reference-counting on non-Main IDs is forbidden). */
|
||||||
BKE_library_foreach_ID_link(
|
BKE_library_foreach_ID_link(
|
||||||
nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP);
|
nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP);
|
||||||
|
|
||||||
/* Append the mesh to 'bmain'.
|
/* Add the mesh to 'bmain'. We do it in a bit longer way since there is no simple and clear way
|
||||||
* We do it a bit longer way since there is no simple and clear way of adding existing data-block
|
* of adding existing data-blocks to the 'bmain'. So we create new empty mesh (which guarantees
|
||||||
* to the 'bmain'. So we allocate new empty mesh in the 'bmain' (which guarantees all the naming
|
* all the naming and order and flags) and move the temporary mesh in place there. */
|
||||||
* and orders and flags) and move the temporary mesh in place there. */
|
|
||||||
Mesh *mesh_in_bmain = BKE_mesh_add(bmain, mesh->id.name + 2);
|
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.
|
* 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->mat = mesh->mat;
|
||||||
mesh_in_bmain->totcol = mesh->totcol;
|
mesh_in_bmain->totcol = mesh->totcol;
|
||||||
|
|
|
@ -408,8 +408,8 @@ template<typename T>
|
||||||
/**
|
/**
|
||||||
* \brief Create an orthographic projection matrix using OpenGL coordinate convention:
|
* \brief Create an orthographic projection matrix using OpenGL coordinate convention:
|
||||||
* Maps each axis range to [-1..1] range for all axes except Z.
|
* 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
|
* The Z axis is almost collapsed to 0 which eliminates the depth component.
|
||||||
* depth testing.
|
* So it should not be used with depth testing.
|
||||||
* The resulting matrix can be used with either #project_point or #transform_point.
|
* 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);
|
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;
|
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 x_delta = right - left;
|
||||||
const T y_delta = top - bottom;
|
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[3][0] = -(right + left) / x_delta;
|
||||||
mat[1][1] = T(2.0) / y_delta;
|
mat[1][1] = T(2.0) / y_delta;
|
||||||
mat[3][1] = -(top + bottom) / y_delta;
|
mat[3][1] = -(top + bottom) / y_delta;
|
||||||
mat[2][2] = 0.0f;
|
/* Page 17. Choosing an epsilon for 32 bit floating-point precision. */
|
||||||
mat[3][2] = 0.0f;
|
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;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
|
@ -406,6 +406,12 @@ void DepsgraphNodeBuilder::begin_build()
|
||||||
saved_entry_tags_.append_as(op_node);
|
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. */
|
/* Make sure graph has no nodes left from previous state. */
|
||||||
graph_->clear_all_nodes();
|
graph_->clear_all_nodes();
|
||||||
graph_->operations.clear();
|
graph_->operations.clear();
|
||||||
|
@ -537,6 +543,16 @@ void DepsgraphNodeBuilder::tag_previously_tagged_nodes()
|
||||||
* that originally node was explicitly tagged for user update. */
|
* that originally node was explicitly tagged for user update. */
|
||||||
operation_node->tag_update(graph_, DEG_UPDATE_SOURCE_USER_EDIT);
|
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()
|
void DepsgraphNodeBuilder::end_build()
|
||||||
|
|
|
@ -291,9 +291,14 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
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. */
|
* Stored before the graph is re-created so that they can be transferred over. */
|
||||||
Vector<PersistentOperationKey> saved_entry_tags_;
|
Vector<PersistentOperationKey> saved_entry_tags_;
|
||||||
|
Vector<PersistentOperationKey> needs_update_operations_;
|
||||||
|
|
||||||
struct BuilderWalkUserData {
|
struct BuilderWalkUserData {
|
||||||
DepsgraphNodeBuilder *builder;
|
DepsgraphNodeBuilder *builder;
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include "eevee_pipeline.hh"
|
#include "eevee_pipeline.hh"
|
||||||
|
|
||||||
#include "eevee_volume.hh"
|
#include "eevee_volume.hh"
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace blender::eevee {
|
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.*/
|
* way, surfaces that are further away than the far clip plane will still be voxelized.*/
|
||||||
winmat_infinite = main_view.is_persp() ?
|
winmat_infinite = main_view.is_persp() ?
|
||||||
math::projection::perspective_infinite(left, right, bottom, top, near) :
|
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. */
|
/* We still need a bounded projection matrix to get correct froxel location. */
|
||||||
winmat_finite = main_view.is_persp() ?
|
winmat_finite = main_view.is_persp() ?
|
||||||
math::projection::perspective(left, right, bottom, top, near, far) :
|
math::projection::perspective(left, right, bottom, top, near, far) :
|
||||||
|
|
|
@ -111,18 +111,36 @@ static void extract_normals_mesh(const MeshRenderData &mr, MutableSpan<GPUType>
|
||||||
template<typename GPUType>
|
template<typename GPUType>
|
||||||
static void extract_paint_overlay_flags(const MeshRenderData &mr, MutableSpan<GPUType> normals)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const OffsetIndices faces = mr.faces;
|
const OffsetIndices faces = mr.faces;
|
||||||
threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
|
threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
|
||||||
if (!mr.select_poly.is_empty()) {
|
if (!selection.is_empty()) {
|
||||||
const Span<bool> select_poly = mr.select_poly;
|
if (use_face_select) {
|
||||||
for (const int face : range) {
|
for (const int face : range) {
|
||||||
if (select_poly[face]) {
|
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]) {
|
for (const int corner : faces[face]) {
|
||||||
normals[corner].w = 1;
|
if (selection[corner_verts[corner]]) {
|
||||||
|
normals[corner].w = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,8 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
|
||||||
|
|
||||||
params.global_scale = RNA_float_get(op->ptr, "global_scale");
|
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. */
|
/* Take some defaults from the scene, if not specified explicitly. */
|
||||||
Scene *scene = CTX_data_scene(C);
|
Scene *scene = CTX_data_scene(C);
|
||||||
if (params.frame_start == INT_MIN) {
|
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;
|
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;
|
uiLayout *box, *row, *col, *sub;
|
||||||
|
|
||||||
|
@ -185,9 +187,12 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
||||||
IFACE_("Custom Properties"),
|
IFACE_("Custom Properties"),
|
||||||
ICON_NONE);
|
ICON_NONE);
|
||||||
|
|
||||||
sub = uiLayoutColumnWithHeading(col, true, IFACE_("Only"));
|
if (CTX_wm_space_file(C)) {
|
||||||
uiItemR(sub, imfptr, "selected", UI_ITEM_NONE, IFACE_("Selected Objects"), ICON_NONE);
|
sub = uiLayoutColumnWithHeading(col, true, IFACE_("Only"));
|
||||||
uiItemR(sub, imfptr, "visible_objects_only", UI_ITEM_NONE, IFACE_("Visible Objects"), ICON_NONE);
|
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);
|
col = uiLayoutColumn(box, true);
|
||||||
uiItemR(col, imfptr, "evaluation_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
|
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);
|
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)
|
static bool wm_alembic_export_check(bContext * /*C*/, wmOperator *op)
|
||||||
|
@ -365,6 +370,9 @@ void WM_OT_alembic_export(wmOperatorType *ot)
|
||||||
"Flatten Hierarchy",
|
"Flatten Hierarchy",
|
||||||
"Do not preserve objects' parent/children relationship");
|
"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, "uvs", true, "UVs", "Export UVs");
|
||||||
|
|
||||||
RNA_def_boolean(ot->srna, "packuv", true, "Pack UV Islands", "Export UVs with packed island");
|
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>();
|
auto fh = std::make_unique<blender::bke::FileHandlerType>();
|
||||||
STRNCPY(fh->idname, "IO_FH_alembic");
|
STRNCPY(fh->idname, "IO_FH_alembic");
|
||||||
STRNCPY(fh->import_operator, "WM_OT_alembic_import");
|
STRNCPY(fh->import_operator, "WM_OT_alembic_import");
|
||||||
|
STRNCPY(fh->export_operator, "WM_OT_alembic_export");
|
||||||
STRNCPY(fh->label, "Alembic");
|
STRNCPY(fh->label, "Alembic");
|
||||||
STRNCPY(fh->file_extensions_str, ".abc");
|
STRNCPY(fh->file_extensions_str, ".abc");
|
||||||
fh->poll_drop = poll_file_object_drop;
|
fh->poll_drop = poll_file_object_drop;
|
||||||
|
|
|
@ -225,9 +225,7 @@ static const char *node_socket_get_translation_context(const bNodeSocket &socket
|
||||||
return translation_context.data();
|
return translation_context.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void node_socket_add_tooltip_in_node_editor(const bNodeTree &ntree,
|
static void node_socket_add_tooltip_in_node_editor(const bNodeSocket &sock, uiLayout &layout);
|
||||||
const bNodeSocket &sock,
|
|
||||||
uiLayout &layout);
|
|
||||||
|
|
||||||
/** Return true when \a a should be behind \a b and false otherwise. */
|
/** Return true when \a a should be behind \a b and false otherwise. */
|
||||||
static bool compare_node_depth(const bNode *a, const bNode *b)
|
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) {
|
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. */
|
/* Round the socket location to stop it from jiggling. */
|
||||||
input_socket->runtime->location = float2(round(locx), round(locy - NODE_DYS));
|
input_socket->runtime->location = float2(round(locx), round(locy - NODE_DYS));
|
||||||
}
|
}
|
||||||
if (output_socket) {
|
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. */
|
/* Round the socket location to stop it from jiggling. */
|
||||||
output_socket->runtime->location = float2(round(locx + NODE_WIDTH(node)),
|
output_socket->runtime->location = float2(round(locx + NODE_WIDTH(node)),
|
||||||
round(locy - NODE_DYS));
|
round(locy - NODE_DYS));
|
||||||
|
@ -1579,20 +1577,6 @@ static std::optional<std::string> create_socket_inspection_string(
|
||||||
return str;
|
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,
|
static std::string node_socket_get_tooltip(const SpaceNode *snode,
|
||||||
const bNodeTree &ntree,
|
const bNodeTree &ntree,
|
||||||
const bNodeSocket &socket)
|
const bNodeSocket &socket)
|
||||||
|
@ -1647,13 +1631,8 @@ static std::string node_socket_get_tooltip(const SpaceNode *snode,
|
||||||
return output.str();
|
return output.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void node_socket_add_tooltip_in_node_editor(const bNodeTree &ntree,
|
static void node_socket_add_tooltip_in_node_editor(const bNodeSocket &sock, uiLayout &layout)
|
||||||
const bNodeSocket &sock,
|
|
||||||
uiLayout &layout)
|
|
||||||
{
|
{
|
||||||
if (!node_socket_has_tooltip(ntree, sock)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uiLayoutSetTooltipFunc(
|
uiLayoutSetTooltipFunc(
|
||||||
&layout,
|
&layout,
|
||||||
[](bContext *C, void *argN, const char * /*tip*/) {
|
[](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)
|
void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, uiLayout &layout)
|
||||||
{
|
{
|
||||||
if (!node_socket_has_tooltip(ntree, sock)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SocketTooltipData {
|
struct SocketTooltipData {
|
||||||
const bNodeTree *ntree;
|
const bNodeTree *ntree;
|
||||||
const bNodeSocket *socket;
|
const bNodeSocket *socket;
|
||||||
|
@ -1727,10 +1702,6 @@ static void node_socket_draw_nested(const bContext &C,
|
||||||
size_id,
|
size_id,
|
||||||
outline_col_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
|
/* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible
|
||||||
* button on top of them for the tooltip. */
|
* button on top of them for the tooltip. */
|
||||||
const eUIEmbossType old_emboss = UI_block_emboss_get(&block);
|
const eUIEmbossType old_emboss = UI_block_emboss_get(&block);
|
||||||
|
|
|
@ -60,6 +60,8 @@ struct AlembicExportParams {
|
||||||
int ngon_method;
|
int ngon_method;
|
||||||
|
|
||||||
float global_scale;
|
float global_scale;
|
||||||
|
|
||||||
|
char collection[MAX_IDPROP_NAME] = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AlembicImportParams {
|
struct AlembicImportParams {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "BKE_context.hh"
|
#include "BKE_context.hh"
|
||||||
#include "BKE_global.hh"
|
#include "BKE_global.hh"
|
||||||
|
#include "BKE_lib_id.hh"
|
||||||
#include "BKE_main.hh"
|
#include "BKE_main.hh"
|
||||||
#include "BKE_scene.hh"
|
#include "BKE_scene.hh"
|
||||||
|
|
||||||
|
@ -49,14 +50,27 @@ struct ExportJobData {
|
||||||
namespace blender::io::alembic {
|
namespace blender::io::alembic {
|
||||||
|
|
||||||
/* Construct the depsgraph for exporting. */
|
/* 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) {
|
if (strlen(job->params.collection) > 0) {
|
||||||
DEG_graph_build_from_view_layer(depsgraph);
|
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 {
|
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)
|
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
|
* 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). */
|
* 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;
|
bool export_ok = false;
|
||||||
if (as_background_job) {
|
if (as_background_job) {
|
||||||
|
|
|
@ -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 (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;
|
return seq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue