Initial Grease Pencil 3.0 stage #106848

Merged
Falk David merged 224 commits from filedescriptor/blender:grease-pencil-v3 into main 2023-05-30 11:14:22 +02:00
7 changed files with 534 additions and 499 deletions
Showing only changes of commit c8dc81e8e4 - Show all commits

View File

@ -31,267 +31,58 @@ class Layer;
* A TreeNode represents one node in the layer tree. * A TreeNode represents one node in the layer tree.
* It can either be a layer or a group. The node has zero children if it is a layer or zero or more * It can either be a layer or a group. The node has zero children if it is a layer or zero or more
* children if it is a group. * children if it is a group.
* This class is mainly used for iteration over the layer tree.
*/ */
class TreeNode : public ::GreasePencilLayerTreeNode, NonMovable { class TreeNode : public ::GreasePencilLayerTreeNode, NonMovable {
using TreeNodeIterFn = FunctionRef<void(TreeNode &)>;
using LayerIterFn = FunctionRef<void(Layer &)>;
using LayerIndexIterFn = FunctionRef<void(uint64_t, Layer &)>;
protected:
Vector<std::unique_ptr<TreeNode>> children_;
public: public:
TreeNode(GreasePencilLayerTreeNodeType type) explicit TreeNode(GreasePencilLayerTreeNodeType type);
{ explicit TreeNode(GreasePencilLayerTreeNodeType type, StringRefNull name);
this->type = type; TreeNode(const TreeNode &other);
this->name = nullptr;
}
TreeNode(GreasePencilLayerTreeNodeType type, StringRefNull name)
{
this->type = type;
this->name = BLI_strdup(name.c_str());
}
TreeNode(const TreeNode &other) : TreeNode(GreasePencilLayerTreeNodeType(other.type))
{
if (other.name) {
this->name = BLI_strdup(other.name);
}
}
TreeNode &operator=(const TreeNode &other) = delete; TreeNode &operator=(const TreeNode &other) = delete;
virtual ~TreeNode() virtual ~TreeNode();
{
if (this->name) {
MEM_freeN(this->name);
}
}
public: public:
class PreOrderRange { Vector<std::unique_ptr<TreeNode>> children;
class Iterator {
using iterator_category = std::forward_iterator_tag;
private:
Stack<TreeNode *> next_node_;
public:
explicit Iterator(TreeNode *root)
{
if (root != nullptr) {
for (auto it = root->children_.rbegin(); it != root->children_.rend(); it++) {
next_node_.push((*it).get());
}
}
}
TreeNode &operator*()
{
return *next_node_.peek();
}
const TreeNode &operator*() const
{
return *next_node_.peek();
}
Iterator &operator++()
{
BLI_assert(!next_node_.is_empty());
TreeNode &next_node = *next_node_.pop();
for (auto it = next_node.children_.rbegin(); it != next_node.children_.rend(); it++) {
next_node_.push((*it).get());
}
return *this;
}
Iterator operator++(int)
{
Iterator old = *this;
operator++();
return old;
}
bool operator==(Iterator &other) const
{
if (next_node_.size() == 0) {
return other.next_node_.size() == 0;
}
return (next_node_.size() == other.next_node_.size()) &&
next_node_.peek() == other.next_node_.peek();
}
bool operator!=(Iterator &other) const
{
return !(*this == other);
}
};
private:
TreeNode *_root;
public:
explicit PreOrderRange(TreeNode *root) : _root(root) {}
Iterator begin()
{
return Iterator(_root);
}
Iterator end()
{
return Iterator(nullptr);
}
};
class PreOrderIndexRange {
public:
struct Item {
const int64_t index;
TreeNode &node;
};
private:
class Iterator {
using iterator_category = std::forward_iterator_tag;
private:
int64_t current_index_;
Stack<TreeNode *> next_node_;
public:
explicit Iterator(TreeNode *root)
{
current_index_ = 0;
if (root != nullptr) {
for (auto it = root->children_.rbegin(); it != root->children_.rend(); it++) {
next_node_.push((*it).get());
}
}
}
Item operator*()
{
return {current_index_, *next_node_.peek()};
}
Iterator &operator++()
{
BLI_assert(!next_node_.is_empty());
TreeNode &next_node = *next_node_.pop();
current_index_++;
for (auto it = next_node.children_.rbegin(); it != next_node.children_.rend(); it++) {
next_node_.push((*it).get());
}
return *this;
}
Iterator operator++(int)
{
Iterator old = *this;
operator++();
return old;
}
bool operator==(Iterator &other) const
{
if (next_node_.size() == 0) {
return other.next_node_.size() == 0;
}
return (next_node_.size() == other.next_node_.size()) &&
next_node_.peek() == other.next_node_.peek() &&
current_index_ == other.current_index_;
}
bool operator!=(Iterator &other) const
{
return !(*this == other);
}
};
private:
TreeNode *_root;
public:
explicit PreOrderIndexRange(TreeNode *root) : _root(root) {}
Iterator begin()
{
return Iterator(_root);
}
Iterator end()
{
return Iterator(nullptr);
}
};
public: public:
/**
* \returns true if this node is a LayerGroup.
*/
constexpr bool is_group() const constexpr bool is_group() const
{ {
return this->type == GREASE_PENCIL_LAYER_TREE_GROUP; return this->type == GREASE_PENCIL_LAYER_TREE_GROUP;
} }
/**
* \returns true if this node is a Layer.
*/
constexpr bool is_layer() const constexpr bool is_layer() const
{ {
return this->type == GREASE_PENCIL_LAYER_TREE_LEAF; return this->type == GREASE_PENCIL_LAYER_TREE_LEAF;
} }
LayerGroup &as_group(); /**
Layer &as_layer(); * \returns this tree node as a LayerGroup.
* \note This results in undefined behavior if the node is not a LayerGroup.
*/
const LayerGroup &as_group() const;
/**
* \returns this tree node as a Layer.
* \note This results in undefined behavior if the node is not a Layer.
*/
const Layer &as_layer() const; const Layer &as_layer() const;
int total_num_children() const /**
{ * \returns this tree node as a mutable LayerGroup.
int total = 0; * \note This results in undefined behavior if the node is not a LayerGroup.
Stack<TreeNode *> stack; */
for (auto it = this->children_.rbegin(); it != this->children_.rend(); it++) { LayerGroup &as_group_for_write();
stack.push((*it).get());
}
while (!stack.is_empty()) {
TreeNode &next_node = *stack.pop();
total++;
for (auto it = next_node.children_.rbegin(); it != next_node.children_.rend(); it++) {
stack.push((*it).get());
}
}
return total;
}
PreOrderRange children_in_pre_order(); /**
PreOrderIndexRange children_with_index_in_pre_order(); * \returns this tree node as a mutable Layer.
* \note This results in undefined behavior if the node is not a Layer.
void foreach_children_pre_order(TreeNodeIterFn function) */
{ Layer &as_layer_for_write();
Review

The _for_write() suffix doesn't seem super helpful here TBH, but I guess it doesn't hurt.

The `_for_write()` suffix doesn't seem super helpful here TBH, but I guess it doesn't hurt.
for (auto &child : children_) {
child->foreach_children_pre_order_recursive_(function);
}
}
void foreach_layer_pre_order(LayerIterFn function)
{
for (auto &child : children_) {
child->foreach_layer_pre_order_recursive_(function);
}
}
private:
void foreach_children_pre_order_recursive_(TreeNodeIterFn function)
{
function(*this);
for (auto &child : children_) {
child->foreach_children_pre_order_recursive_(function);
}
}
void foreach_layer_pre_order_recursive_(LayerIterFn function)
{
if (this->is_layer()) {
function(this->as_layer());
}
for (auto &child : children_) {
child->foreach_layer_pre_order_recursive_(function);
}
}
}; };
/** /**
@ -330,150 +121,110 @@ class Layer : public TreeNode, ::GreasePencilLayer {
public: public:
Layer() : TreeNode(GREASE_PENCIL_LAYER_TREE_LEAF), frames_() {} Layer() : TreeNode(GREASE_PENCIL_LAYER_TREE_LEAF), frames_() {}
Layer(StringRefNull name) : TreeNode(GREASE_PENCIL_LAYER_TREE_LEAF, name), frames_() {} explicit Layer(StringRefNull name) : TreeNode(GREASE_PENCIL_LAYER_TREE_LEAF, name), frames_() {}
Layer(const Layer &other) : TreeNode(other) Layer(const Layer &other);
{
frames_ = other.frames_;
}
const Map<int, GreasePencilFrame> &frames() const
{
return frames_;
}
Map<int, GreasePencilFrame> &frames_for_write()
{
sorted_keys_cache_.tag_dirty();
return frames_;
}
bool insert_frame(int frame_number, GreasePencilFrame &frame)
{
sorted_keys_cache_.tag_dirty();
return frames_for_write().add(frame_number, frame);
}
bool insert_frame(int frame_number, GreasePencilFrame &&frame)
{
sorted_keys_cache_.tag_dirty();
return frames_for_write().add(frame_number, frame);
}
bool overwrite_frame(int frame_number, GreasePencilFrame &frame)
{
sorted_keys_cache_.tag_dirty();
return frames_for_write().add_overwrite(frame_number, frame);
}
bool overwrite_frame(int frame_number, GreasePencilFrame &&frame)
{
sorted_keys_cache_.tag_dirty();
return frames_for_write().add_overwrite(frame_number, frame);
}
Span<int> sorted_keys() const
{
sorted_keys_cache_.ensure([&](Vector<int> &r_data) {
r_data.clear_and_shrink();
r_data.reserve(frames().size());
for (int64_t key : frames().keys()) {
r_data.append(key);
}
std::sort(r_data.begin(), r_data.end());
});
return sorted_keys_cache_.data().as_span();
}
/** /**
* Return the index of the drawing at frame \a frame or -1 if there is no drawing. * \returns the frames mapping.
*/ */
int drawing_at(int frame) const const Map<int, GreasePencilFrame> &frames() const;
{ Map<int, GreasePencilFrame> &frames_for_write();
Span<int> sorted_keys = this->sorted_keys();
/* Before the first drawing, return no drawing. */ /**
if (frame < sorted_keys.first()) { * Inserts the frame into the layer. Fails if there exists a frame at \a frame_number already.
return -1; * \returns true on success.
} */
/* After or at the last drawing, return the last drawing. */ bool insert_frame(int frame_number, GreasePencilFrame &frame);
if (frame >= sorted_keys.last()) { bool insert_frame(int frame_number, GreasePencilFrame &&frame);
return this->frames().lookup(sorted_keys.last()).drawing_index;
} /**
/* Search for the drawing. upper_bound will get the drawing just after. */ * Inserts the frame into the layer. If there exists a frame at \a frame_number already, it is
auto it = std::upper_bound(sorted_keys.begin(), sorted_keys.end(), frame); * overwritten.
if (it == sorted_keys.end() || it == sorted_keys.begin()) { * \returns true on success.
return -1; */
} bool overwrite_frame(int frame_number, GreasePencilFrame &frame);
return this->frames().lookup(*std::prev(it)).drawing_index; bool overwrite_frame(int frame_number, GreasePencilFrame &&frame);
}
/**
* Returns the sorted (start) frame numbers of the frames of this layer.
* \note This will cache the keys lazily.
*/
Span<int> sorted_keys() const;
/**
* \returns the index of the drawing at frame \a frame or -1 if there is no drawing.
*/
int drawing_index_at(int frame) const;
}; };
/** /**
* A LayerGroup is a grouping of zero or more Layers. * A LayerGroup is a grouping of zero or more Layers.
*/ */
class LayerGroup : public TreeNode { class LayerGroup : public TreeNode {
using TreeNodeIterFn = FunctionRef<void(TreeNode &)>;
using TreeNodeIndexIterFn = FunctionRef<void(int64_t, TreeNode &)>;
using LayerIterFn = FunctionRef<void(Layer &)>;
using LayerIndexIterFn = FunctionRef<void(int64_t, Layer &)>;
public: public:
LayerGroup() : TreeNode(GREASE_PENCIL_LAYER_TREE_GROUP) {} LayerGroup() : TreeNode(GREASE_PENCIL_LAYER_TREE_GROUP) {}
explicit LayerGroup(const StringRefNull name) : TreeNode(GREASE_PENCIL_LAYER_TREE_GROUP, name) {} explicit LayerGroup(const StringRefNull name) : TreeNode(GREASE_PENCIL_LAYER_TREE_GROUP, name) {}
LayerGroup(const LayerGroup &other) : TreeNode(other) LayerGroup(const LayerGroup &other);
{
children_.reserve(other.children_.size());
for (const std::unique_ptr<TreeNode> &elem : other.children_) {
if (elem.get()->is_group()) {
children_.append(std::make_unique<LayerGroup>(elem.get()->as_group()));
}
else if (elem.get()->is_layer()) {
children_.append(std::make_unique<Layer>(elem.get()->as_layer()));
}
}
}
public: public:
void add_group(LayerGroup &group) /**
{ * Adds a group at the end of this group.
children_.append(std::make_unique<LayerGroup>(group)); */
} void add_group(LayerGroup &group);
void add_group(LayerGroup &&group);
void add_group(LayerGroup &&group) /**
{ * Adds a layer at the end of this group and returns it.
children_.append(std::make_unique<LayerGroup>(group)); */
} Layer &add_layer(Layer &layer);
Layer &add_layer(Layer &&layer);
Layer &add_layer(Layer &layer) /**
{ * Returns the number of direct children in this group.
int64_t index = children_.append_and_get_index(std::make_unique<Layer>(layer)); */
return children_[index].get()->as_layer(); int64_t num_direct_children() const;
}
Layer &add_layer(Layer &&layer) /**
{ * Returns the total number of children in this group.
int64_t index = children_.append_and_get_index(std::make_unique<Layer>(layer)); */
return children_[index].get()->as_layer(); int64_t num_children_total() const;
}
int num_children() const /**
{ * Removes a child from the group by index.
return children_.size(); */
} void remove_child(int64_t index);
void remove_child(int64_t index) /**
{ * Calls \a function on every `TreeNode` in this group.
BLI_assert(index >= 0 && index < children_.size()); */
children_.remove(index); void foreach_children_pre_order(TreeNodeIterFn function);
} void foreach_children_with_index_pre_order(TreeNodeIndexIterFn function);
/**
* Returns a `Vector` of pointers to all the `TreeNode`s in this group.
*/
Vector<TreeNode *> children_in_pre_order() const;
/**
* Returns a `Vector` of pointers to all the `Layers`s in this group.
*/
Vector<Layer *> layers_in_pre_order() const;
}; };
namespace convert { namespace convert {
void legacy_gpencil_frame_to_grease_pencil_drawing(GreasePencilDrawing &drawing, bGPDframe &gpf); void legacy_gpencil_frame_to_grease_pencil_drawing(GreasePencilDrawing &drawing, bGPDframe &gpf);
void legacy_gpencil_to_grease_pencil(GreasePencil &grease_pencil, bGPdata &gpd); void legacy_gpencil_to_grease_pencil(GreasePencil &grease_pencil, bGPdata &gpd);
} // namespace convert } // namespace convert
} // namespace greasepencil } // namespace greasepencil
using namespace blender::bke::greasepencil;
struct StrokePoint { struct StrokePoint {
float3 position; float3 position;
float radius; float radius;
@ -494,71 +245,34 @@ class GreasePencilDrawingRuntime {
}; };
class GreasePencilRuntime { class GreasePencilRuntime {
private: public:
LayerGroup root_group_; mutable SharedCache<Vector<greasepencil::Layer *>> layer_cache_;
private:
greasepencil::LayerGroup root_group_;
int active_layer_index_ = -1; int active_layer_index_ = -1;
Layer *active_layer_ = nullptr;
public: public:
GreasePencilRuntime() {} GreasePencilRuntime() {}
GreasePencilRuntime(const GreasePencilRuntime &other) GreasePencilRuntime(const GreasePencilRuntime &other);
: root_group_(other.root_group_), active_layer_index_(other.active_layer_index_)
{
active_layer_ = get_active_layer_from_index(other.active_layer_index_);
}
/* TODO: There should be a const version of this for reads and a mutable version of this for public:
* writes. */ const greasepencil::LayerGroup &root_group() const;
LayerGroup &root_group() greasepencil::LayerGroup &root_group_for_write();
{
return root_group_;
}
bool has_active_layer() const bool has_active_layer() const;
{ const greasepencil::Layer &active_layer() const;
return active_layer_ != nullptr; greasepencil::Layer &active_layer_for_write() const;
} void set_active_layer_index(int index);
int active_layer_index() const;
const Layer &active_layer() const void ensure_layer_cache() const;
{
BLI_assert(active_layer_ != nullptr);
return *active_layer_;
}
Layer &active_layer_for_write() const
{
BLI_assert(active_layer_ != nullptr);
return *active_layer_;
}
void set_active_layer(int index)
{
active_layer_index_ = index;
active_layer_ = get_active_layer_from_index(index);
}
int active_layer_index() const
{
return active_layer_index_;
}
public: public:
void *batch_cache = nullptr; void *batch_cache = nullptr;
private: private:
Layer *get_active_layer_from_index(int index) greasepencil::Layer *get_active_layer_from_index(int index) const;
{
if (index < 0) {
return nullptr;
}
for (auto item : this->root_group().children_with_index_in_pre_order()) {
if (item.node.is_layer() && item.index == index) {
return &item.node.as_layer();
}
}
return nullptr;
}
}; };
} // namespace blender::bke } // namespace blender::bke

View File

@ -250,24 +250,36 @@ IDTypeInfo IDType_ID_GP = {
namespace blender::bke::greasepencil { namespace blender::bke::greasepencil {
TreeNode::PreOrderRange TreeNode::children_in_pre_order() TreeNode::TreeNode(GreasePencilLayerTreeNodeType type)
{ {
return TreeNode::PreOrderRange(this); this->type = type;
this->name = nullptr;
} }
TreeNode::PreOrderIndexRange TreeNode::children_with_index_in_pre_order() TreeNode::TreeNode(GreasePencilLayerTreeNodeType type, StringRefNull name)
{ {
return TreeNode::PreOrderIndexRange(this); this->type = type;
this->name = BLI_strdup(name.c_str());
} }
LayerGroup &TreeNode::as_group() TreeNode::TreeNode(const TreeNode &other)
: TreeNode::TreeNode(GreasePencilLayerTreeNodeType(other.type))
{ {
return *static_cast<LayerGroup *>(this); if (other.name) {
this->name = BLI_strdup(other.name);
}
} }
Layer &TreeNode::as_layer() TreeNode::~TreeNode()
{ {
return *static_cast<Layer *>(this); if (this->name) {
MEM_freeN(this->name);
}
}
const LayerGroup &TreeNode::as_group() const
{
return *static_cast<const LayerGroup *>(this);
} }
const Layer &TreeNode::as_layer() const const Layer &TreeNode::as_layer() const
@ -275,8 +287,282 @@ const Layer &TreeNode::as_layer() const
return *static_cast<const Layer *>(this); return *static_cast<const Layer *>(this);
} }
LayerGroup &TreeNode::as_group_for_write()
{
return *static_cast<LayerGroup *>(this);
}
Layer &TreeNode::as_layer_for_write()
{
return *static_cast<Layer *>(this);
}
Layer::Layer(const Layer &other) : TreeNode::TreeNode(other)
{
this->frames_ = other.frames_;
}
const Map<int, GreasePencilFrame> &Layer::frames() const
{
return this->frames_;
}
Map<int, GreasePencilFrame> &Layer::frames_for_write()
{
this->sorted_keys_cache_.tag_dirty();
return this->frames_;
}
bool Layer::insert_frame(int frame_number, GreasePencilFrame &frame)
{
this->sorted_keys_cache_.tag_dirty();
return this->frames_for_write().add(frame_number, frame);
}
bool Layer::insert_frame(int frame_number, GreasePencilFrame &&frame)
{
this->sorted_keys_cache_.tag_dirty();
return this->frames_for_write().add(frame_number, frame);
}
bool Layer::overwrite_frame(int frame_number, GreasePencilFrame &frame)
{
this->sorted_keys_cache_.tag_dirty();
return this->frames_for_write().add_overwrite(frame_number, frame);
}
bool Layer::overwrite_frame(int frame_number, GreasePencilFrame &&frame)
{
this->sorted_keys_cache_.tag_dirty();
return this->frames_for_write().add_overwrite(frame_number, frame);
}
Span<int> Layer::sorted_keys() const
{
this->sorted_keys_cache_.ensure([&](Vector<int> &r_data) {
r_data.clear_and_shrink();
r_data.reserve(this->frames().size());
for (int64_t key : this->frames().keys()) {
r_data.append(key);
}
std::sort(r_data.begin(), r_data.end());
});
return this->sorted_keys_cache_.data().as_span();
}
int Layer::drawing_index_at(int frame) const
{
Span<int> sorted_keys = this->sorted_keys();
/* Before the first drawing, return no drawing. */
if (frame < sorted_keys.first()) {
return -1;
}
/* After or at the last drawing, return the last drawing. */
if (frame >= sorted_keys.last()) {
return this->frames().lookup(sorted_keys.last()).drawing_index;
}
/* Search for the drawing. upper_bound will get the drawing just after. */
auto it = std::upper_bound(sorted_keys.begin(), sorted_keys.end(), frame);
if (it == sorted_keys.end() || it == sorted_keys.begin()) {
return -1;
}
return this->frames().lookup(*std::prev(it)).drawing_index;
}
LayerGroup::LayerGroup(const LayerGroup &other) : TreeNode(other)
{
this->children.reserve(other.children.size());
for (const std::unique_ptr<TreeNode> &elem : other.children) {
if (elem.get()->is_group()) {
this->children.append(std::make_unique<LayerGroup>(elem.get()->as_group()));
}
else if (elem.get()->is_layer()) {
this->children.append(std::make_unique<Layer>(elem.get()->as_layer()));
}
}
}
void LayerGroup::add_group(LayerGroup &group)
{
this->children.append(std::make_unique<LayerGroup>(group));
}
void LayerGroup::add_group(LayerGroup &&group)
{
this->children.append(std::make_unique<LayerGroup>(group));
}
Layer &LayerGroup::add_layer(Layer &layer)
{
int64_t index = children.append_and_get_index(std::make_unique<Layer>(layer));
return children[index].get()->as_layer_for_write();
}
Layer &LayerGroup::add_layer(Layer &&layer)
{
int64_t index = children.append_and_get_index(std::make_unique<Layer>(layer));
return children[index].get()->as_layer_for_write();
}
int64_t LayerGroup::num_direct_children() const
{
return children.size();
}
int64_t LayerGroup::num_children_total() const
{
int64_t total = 0;
Stack<TreeNode *> stack;
for (auto it = this->children.rbegin(); it != this->children.rend(); it++) {
stack.push((*it).get());
}
while (!stack.is_empty()) {
TreeNode &next_node = *stack.pop();
total++;
for (auto it = next_node.children.rbegin(); it != next_node.children.rend(); it++) {
stack.push((*it).get());
}
}
return total;
}
void LayerGroup::remove_child(int64_t index)
{
BLI_assert(index >= 0 && index < this->children.size());
this->children.remove(index);
}
void LayerGroup::foreach_children_pre_order(TreeNodeIterFn function)
{
for (auto &child : this->children) {
function(*child);
if (child->is_group()) {
child->as_group_for_write().foreach_children_pre_order(function);
}
}
}
void LayerGroup::foreach_children_with_index_pre_order(TreeNodeIndexIterFn function)
{
Vector<TreeNode *> children = this->children_in_pre_order();
for (const int64_t i : IndexRange(children.size())) {
function(i, *children[i]);
}
}
Vector<TreeNode *> LayerGroup::children_in_pre_order() const
{
Vector<TreeNode *> children;
Stack<TreeNode *> stack;
for (auto it = this->children.rbegin(); it != this->children.rend(); it++) {
stack.push((*it).get());
}
while (!stack.is_empty()) {
TreeNode &next_node = *stack.pop();
children.append(&next_node);
for (auto it = next_node.children.rbegin(); it != next_node.children.rend(); it++) {
stack.push((*it).get());
}
}
return children;
}
Vector<Layer *> LayerGroup::layers_in_pre_order() const
{
Vector<Layer *> layers;
Stack<TreeNode *> stack;
for (auto it = this->children.rbegin(); it != this->children.rend(); it++) {
stack.push((*it).get());
}
while (!stack.is_empty()) {
TreeNode &next_node = *stack.pop();
if (next_node.is_layer()) {
layers.append(&next_node.as_layer_for_write());
}
else {
for (auto it = next_node.children.rbegin(); it != next_node.children.rend(); it++) {
stack.push((*it).get());
}
}
}
return layers;
}
} // namespace blender::bke::greasepencil } // namespace blender::bke::greasepencil
/* ------------------------------------------------------------------- */
/** \name GreasePencilRuntime functions
* \{ */
namespace blender::bke {
GreasePencilRuntime::GreasePencilRuntime(const GreasePencilRuntime &other)
: root_group_(other.root_group_), active_layer_index_(other.active_layer_index_)
{
layer_cache_ = other.layer_cache_;
}
const greasepencil::LayerGroup &GreasePencilRuntime::root_group() const
{
return this->root_group_;
}
greasepencil::LayerGroup &GreasePencilRuntime::root_group_for_write()
{
this->layer_cache_.tag_dirty();
return this->root_group_;
}
bool GreasePencilRuntime::has_active_layer() const
{
return this->active_layer_index_ >= 0;
}
const greasepencil::Layer &GreasePencilRuntime::active_layer() const
{
BLI_assert(this->active_layer_index_ >= 0);
return *get_active_layer_from_index(this->active_layer_index_);
}
greasepencil::Layer &GreasePencilRuntime::active_layer_for_write() const
{
BLI_assert(this->active_layer_index_ >= 0);
return *get_active_layer_from_index(this->active_layer_index_);
}
void GreasePencilRuntime::set_active_layer_index(int index)
{
this->active_layer_index_ = index;
}
int GreasePencilRuntime::active_layer_index() const
{
return this->active_layer_index_;
}
void GreasePencilRuntime::ensure_layer_cache() const
{
this->layer_cache_.ensure([this](Vector<greasepencil::Layer *> &data) {
data = this->root_group_.layers_in_pre_order();
});
}
greasepencil::Layer *GreasePencilRuntime::get_active_layer_from_index(int index) const
{
this->ensure_layer_cache();
return this->layer_cache_.data()[index];
}
} // namespace blender::bke
/** \} */
/* ------------------------------------------------------------------- */
/** \name Grease Pencil kernel functions
* \{ */
void *BKE_grease_pencil_add(Main *bmain, const char *name) void *BKE_grease_pencil_add(Main *bmain, const char *name)
{ {
GreasePencil *grease_pencil = static_cast<GreasePencil *>(BKE_id_new(bmain, ID_GP, name)); GreasePencil *grease_pencil = static_cast<GreasePencil *>(BKE_id_new(bmain, ID_GP, name));
@ -356,7 +642,11 @@ void BKE_grease_pencil_data_update(struct Depsgraph * /*depsgraph*/,
BKE_object_eval_assign_data(object, &grease_pencil_eval->id, true); BKE_object_eval_assign_data(object, &grease_pencil_eval->id, true);
} }
/* Draw Cache */ /** \} */
/* ------------------------------------------------------------------- */
/** \name Draw Cache
* \{ */
void (*BKE_grease_pencil_batch_cache_dirty_tag_cb)(GreasePencil *grease_pencil, void (*BKE_grease_pencil_batch_cache_dirty_tag_cb)(GreasePencil *grease_pencil,
int mode) = nullptr; int mode) = nullptr;
@ -376,7 +666,11 @@ void BKE_grease_pencil_batch_cache_free(GreasePencil *grease_pencil)
} }
} }
/* GreasePencilDrawing API */ /** \} */
/* ------------------------------------------------------------------- */
/** \name Grease Pencil Drawing API
* \{ */
blender::Span<blender::uint3> GreasePencilDrawing::triangles() const blender::Span<blender::uint3> GreasePencilDrawing::triangles() const
{ {
@ -454,7 +748,11 @@ blender::Span<blender::bke::StrokePoint> GreasePencilDrawing::stroke_buffer()
return this->runtime->stroke_cache.as_span(); return this->runtime->stroke_cache.as_span();
} }
/* GreasePencil API */ /** \} */
/* ------------------------------------------------------------------- */
/** \name Grease Pencil data-block API
* \{ */
static void grease_pencil_grow_drawing_array_by(GreasePencil &self, const int add_capacity) static void grease_pencil_grow_drawing_array_by(GreasePencil &self, const int add_capacity)
{ {
@ -533,18 +831,17 @@ void GreasePencil::remove_drawing(int index_to_remove)
/* Move the drawing that should be removed to the last index. */ /* Move the drawing that should be removed to the last index. */
const int last_drawing_index = this->drawing_array_size - 1; const int last_drawing_index = this->drawing_array_size - 1;
if (index_to_remove != last_drawing_index) { if (index_to_remove != last_drawing_index) {
this->root_group().foreach_layer_pre_order( for (Layer *layer : this->layers_for_write()) {
[last_drawing_index, index_to_remove](Layer &layer) { blender::Map<int, GreasePencilFrame> &frames = layer->frames_for_write();
blender::Map<int, GreasePencilFrame> &frames = layer.frames_for_write(); for (auto [key, value] : frames.items()) {
for (auto [key, value] : frames.items()) { if (value.drawing_index == last_drawing_index) {
if (value.drawing_index == last_drawing_index) { value.drawing_index = index_to_remove;
value.drawing_index = index_to_remove; }
} else if (value.drawing_index == index_to_remove) {
else if (value.drawing_index == index_to_remove) { value.drawing_index = last_drawing_index;
value.drawing_index = last_drawing_index; }
} }
} }
});
std::swap(this->drawings_for_write()[index_to_remove], std::swap(this->drawings_for_write()[index_to_remove],
this->drawings_for_write()[last_drawing_index]); this->drawings_for_write()[last_drawing_index]);
} }
@ -571,12 +868,12 @@ void GreasePencil::remove_drawing(int index_to_remove)
} }
/* Remove any frame that points to the last drawing. */ /* Remove any frame that points to the last drawing. */
this->root_group().foreach_layer_pre_order([last_drawing_index](Layer &layer) { for (Layer *layer : this->layers_for_write()) {
blender::Map<int, GreasePencilFrame> &frames = layer.frames_for_write(); blender::Map<int, GreasePencilFrame> &frames = layer->frames_for_write();
frames.remove_if([last_drawing_index](auto item) { frames.remove_if([last_drawing_index](auto item) {
return item.value.drawing_index == last_drawing_index; return item.value.drawing_index == last_drawing_index;
}); });
}); }
/* Shrink drawing array. */ /* Shrink drawing array. */
grease_pencil_shrink_drawing_array_by(*this, 1); grease_pencil_shrink_drawing_array_by(*this, 1);
@ -588,12 +885,8 @@ void GreasePencil::foreach_visible_drawing(
using namespace blender::bke::greasepencil; using namespace blender::bke::greasepencil;
blender::Span<GreasePencilDrawingOrReference *> drawings = this->drawings(); blender::Span<GreasePencilDrawingOrReference *> drawings = this->drawings();
for (TreeNode &node : this->root_group().children_in_pre_order()) { for (const Layer *layer : this->layers()) {
if (!node.is_layer()) { int index = layer->drawing_index_at(frame);
continue;
}
Layer &layer = node.as_layer();
int index = layer.drawing_at(frame);
if (index == -1) { if (index == -1) {
continue; continue;
} }
@ -608,12 +901,41 @@ void GreasePencil::foreach_visible_drawing(
} }
} }
blender::bke::greasepencil::LayerGroup &GreasePencil::root_group() const blender::bke::greasepencil::LayerGroup &GreasePencil::root_group() const
{ {
BLI_assert(this->runtime != nullptr); BLI_assert(this->runtime != nullptr);
return this->runtime->root_group(); return this->runtime->root_group();
} }
blender::bke::greasepencil::LayerGroup &GreasePencil::root_group_for_write()
{
BLI_assert(this->runtime != nullptr);
return this->runtime->root_group_for_write();
}
blender::Span<const blender::bke::greasepencil::Layer *> GreasePencil::layers() const
{
this->runtime->ensure_layer_cache();
return this->runtime->layer_cache_.data();
}
blender::Span<blender::bke::greasepencil::Layer *> GreasePencil::layers_for_write()
{
this->runtime->ensure_layer_cache();
return this->runtime->layer_cache_.data();
}
void GreasePencil::tag_layer_tree_changed()
{
this->runtime->layer_cache_.tag_dirty();
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Drawing array storage functions
* \{ */
void GreasePencil::read_drawing_array(BlendDataReader *reader) void GreasePencil::read_drawing_array(BlendDataReader *reader)
{ {
BLO_read_pointer_array(reader, (void **)&this->drawing_array); BLO_read_pointer_array(reader, (void **)&this->drawing_array);
@ -692,7 +1014,13 @@ void GreasePencil::free_drawing_array()
this->drawing_array_size = 0; this->drawing_array_size = 0;
} }
static void save_tree_node_to_storage(blender::bke::greasepencil::TreeNode &node, /** \} */
/* ------------------------------------------------------------------- */
/** \name Layer Tree storage functions
* \{ */
static void save_tree_node_to_storage(const blender::bke::greasepencil::TreeNode &node,
GreasePencilLayerTreeNode *dst) GreasePencilLayerTreeNode *dst)
{ {
dst->type = node.type; dst->type = node.type;
@ -703,7 +1031,7 @@ static void save_tree_node_to_storage(blender::bke::greasepencil::TreeNode &node
} }
} }
static void save_layer_to_storage(blender::bke::greasepencil::Layer &node, static void save_layer_to_storage(const blender::bke::greasepencil::Layer &node,
GreasePencilLayerTreeNode **dst) GreasePencilLayerTreeNode **dst)
{ {
using namespace blender; using namespace blender;
@ -730,7 +1058,7 @@ static void save_layer_to_storage(blender::bke::greasepencil::Layer &node,
*dst = reinterpret_cast<GreasePencilLayerTreeNode *>(new_leaf); *dst = reinterpret_cast<GreasePencilLayerTreeNode *>(new_leaf);
} }
static void save_layer_group_to_storage(blender::bke::greasepencil::LayerGroup &node, static void save_layer_group_to_storage(const blender::bke::greasepencil::LayerGroup &node,
GreasePencilLayerTreeNode **dst) GreasePencilLayerTreeNode **dst)
{ {
GreasePencilLayerTreeGroup *new_group = MEM_cnew<GreasePencilLayerTreeGroup>(__func__); GreasePencilLayerTreeGroup *new_group = MEM_cnew<GreasePencilLayerTreeGroup>(__func__);
@ -738,7 +1066,7 @@ static void save_layer_group_to_storage(blender::bke::greasepencil::LayerGroup &
save_tree_node_to_storage(node, &new_group->base); save_tree_node_to_storage(node, &new_group->base);
/* Save number of children. */ /* Save number of children. */
new_group->children_num = node.num_children(); new_group->children_num = node.num_direct_children();
/* Store pointer. */ /* Store pointer. */
*dst = reinterpret_cast<GreasePencilLayerTreeNode *>(new_group); *dst = reinterpret_cast<GreasePencilLayerTreeNode *>(new_group);
@ -748,25 +1076,24 @@ void GreasePencil::save_layer_tree_to_storage()
{ {
using namespace blender::bke::greasepencil; using namespace blender::bke::greasepencil;
/* We always store the root group, so we have to add one here. */ /* We always store the root group, so we have to add one here. */
int num_tree_nodes = this->root_group().total_num_children() + 1; int num_tree_nodes = this->root_group_for_write().num_children_total() + 1;
this->layer_tree_storage.nodes_num = num_tree_nodes; this->layer_tree_storage.nodes_num = num_tree_nodes;
this->layer_tree_storage.nodes = MEM_cnew_array<GreasePencilLayerTreeNode *>(num_tree_nodes, this->layer_tree_storage.nodes = MEM_cnew_array<GreasePencilLayerTreeNode *>(num_tree_nodes,
__func__); __func__);
int i = 0; save_layer_group_to_storage(this->root_group_for_write(), &this->layer_tree_storage.nodes[0]);
save_layer_group_to_storage(this->root_group(), &this->layer_tree_storage.nodes[i++]); this->root_group_for_write().foreach_children_with_index_pre_order(
for (TreeNode &node : this->root_group().children_in_pre_order()) { [this](uint64_t i, TreeNode &node) {
GreasePencilLayerTreeNode **dst = &this->layer_tree_storage.nodes[i]; GreasePencilLayerTreeNode **dst = &this->layer_tree_storage.nodes[i + 1];
if (node.is_group()) { if (node.is_group()) {
LayerGroup &group = node.as_group(); const LayerGroup &group = node.as_group();
save_layer_group_to_storage(group, dst); save_layer_group_to_storage(group, dst);
} }
else if (node.is_layer()) { else if (node.is_layer()) {
Layer &layer = node.as_layer(); const Layer &layer = node.as_layer();
save_layer_to_storage(layer, dst); save_layer_to_storage(layer, dst);
} }
i++; });
}
this->layer_tree_storage.active_layer_index = this->runtime->active_layer_index(); this->layer_tree_storage.active_layer_index = this->runtime->active_layer_index();
} }
@ -811,10 +1138,11 @@ void GreasePencil::load_layer_tree_from_storage()
this->layer_tree_storage.nodes[0]); this->layer_tree_storage.nodes[0]);
BLI_assert(root->type == GREASE_PENCIL_LAYER_TREE_GROUP); BLI_assert(root->type == GREASE_PENCIL_LAYER_TREE_GROUP);
for (int i = 0; i < reinterpret_cast<GreasePencilLayerTreeGroup *>(root)->children_num; i++) { for (int i = 0; i < reinterpret_cast<GreasePencilLayerTreeGroup *>(root)->children_num; i++) {
read_layer_node_recursive(this->runtime->root_group(), this->layer_tree_storage.nodes, i + 1); read_layer_node_recursive(
this->runtime->root_group_for_write(), this->layer_tree_storage.nodes, i + 1);
} }
this->runtime->set_active_layer(this->layer_tree_storage.active_layer_index); this->runtime->set_active_layer_index(this->layer_tree_storage.active_layer_index);
} }
void GreasePencil::read_layer_tree_storage(BlendDataReader *reader) void GreasePencil::read_layer_tree_storage(BlendDataReader *reader)
@ -903,3 +1231,5 @@ void GreasePencil::free_layer_tree_storage()
this->layer_tree_storage.nodes = nullptr; this->layer_tree_storage.nodes = nullptr;
this->layer_tree_storage.nodes_num = 0; this->layer_tree_storage.nodes_num = 0;
} }
/** \} */

View File

@ -111,7 +111,7 @@ void legacy_gpencil_to_grease_pencil(GreasePencil &grease_pencil, bGPdata &gpd)
int i = 0, layer_idx = 0; int i = 0, layer_idx = 0;
int active_layer_index = 0; int active_layer_index = 0;
LISTBASE_FOREACH_INDEX (bGPDlayer *, gpl, &gpd.layers, layer_idx) { LISTBASE_FOREACH_INDEX (bGPDlayer *, gpl, &gpd.layers, layer_idx) {
Layer &new_layer = grease_pencil.root_group().add_layer(Layer(gpl->info)); Layer &new_layer = grease_pencil.root_group_for_write().add_layer(Layer(gpl->info));
if ((gpl->flag & GP_LAYER_ACTIVE) != 0) { if ((gpl->flag & GP_LAYER_ACTIVE) != 0) {
active_layer_index = layer_idx; active_layer_index = layer_idx;
} }
@ -135,7 +135,7 @@ void legacy_gpencil_to_grease_pencil(GreasePencil &grease_pencil, bGPdata &gpd)
} }
} }
grease_pencil.runtime->set_active_layer(active_layer_index); grease_pencil.runtime->set_active_layer_index(active_layer_index);
} }
} // namespace blender::bke::greasepencil::convert } // namespace blender::bke::greasepencil::convert

View File

@ -36,10 +36,10 @@ TEST(greasepencil, create_grease_pencil_id)
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(BKE_id_new(ctx.bmain, ID_GP, "GP")); GreasePencil &grease_pencil = *static_cast<GreasePencil *>(BKE_id_new(ctx.bmain, ID_GP, "GP"));
EXPECT_EQ(grease_pencil.drawings().size(), 0); EXPECT_EQ(grease_pencil.drawings().size(), 0);
EXPECT_EQ(grease_pencil.root_group().total_num_children(), 0); EXPECT_EQ(grease_pencil.root_group().num_children_total(), 0);
} }
TEST(greasepencil, set_active_layer) TEST(greasepencil, set_active_layer_index)
{ {
GreasePencilIDTestContext ctx; GreasePencilIDTestContext ctx;
@ -49,14 +49,14 @@ TEST(greasepencil, set_active_layer)
Layer layer1("Layer1"); Layer layer1("Layer1");
Layer layer2("Layer2"); Layer layer2("Layer2");
const Layer &layer1_ref = grease_pencil.root_group().add_layer(std::move(layer1)); const Layer &layer1_ref = grease_pencil.root_group_for_write().add_layer(std::move(layer1));
const Layer &layer2_ref = grease_pencil.root_group().add_layer(std::move(layer2)); const Layer &layer2_ref = grease_pencil.root_group_for_write().add_layer(std::move(layer2));
grease_pencil.runtime->set_active_layer(0); grease_pencil.runtime->set_active_layer_index(0);
EXPECT_TRUE(grease_pencil.runtime->has_active_layer()); EXPECT_TRUE(grease_pencil.runtime->has_active_layer());
EXPECT_STREQ(layer1_ref.name, grease_pencil.runtime->active_layer().name); EXPECT_STREQ(layer1_ref.name, grease_pencil.runtime->active_layer().name);
grease_pencil.runtime->set_active_layer(1); grease_pencil.runtime->set_active_layer_index(1);
EXPECT_TRUE(grease_pencil.runtime->has_active_layer()); EXPECT_TRUE(grease_pencil.runtime->has_active_layer());
EXPECT_STREQ(layer2_ref.name, grease_pencil.runtime->active_layer().name); EXPECT_STREQ(layer2_ref.name, grease_pencil.runtime->active_layer().name);
@ -104,16 +104,16 @@ TEST(greasepencil, remove_drawing)
layer2.insert_frame(0, GreasePencilFrame{1}); layer2.insert_frame(0, GreasePencilFrame{1});
grease_pencil.root_group().add_layer(std::move(layer1)); grease_pencil.root_group_for_write().add_layer(std::move(layer1));
grease_pencil.root_group().add_layer(std::move(layer2)); grease_pencil.root_group_for_write().add_layer(std::move(layer2));
grease_pencil.remove_drawing(1); grease_pencil.remove_drawing(1);
EXPECT_EQ(grease_pencil.drawings().size(), 2); EXPECT_EQ(grease_pencil.drawings().size(), 2);
static int expected_frames_size[] = {2, 0}; static int expected_frames_size[] = {2, 0};
static int expected_frames_pairs_layer0[][2] = {{0, 0}, {20, 1}}; static int expected_frames_pairs_layer0[][2] = {{0, 0}, {20, 1}};
int layer_i = 0; for (const int layer_i : IndexRange(grease_pencil.layers().size())) {
grease_pencil.root_group().foreach_layer_pre_order([&](Layer &layer) { const Layer &layer = *grease_pencil.layers()[layer_i];
EXPECT_EQ(layer.frames().size(), expected_frames_size[layer_i]); EXPECT_EQ(layer.frames().size(), expected_frames_size[layer_i]);
if (layer_i == 0) { if (layer_i == 0) {
for (const int i : IndexRange(2)) { for (const int i : IndexRange(2)) {
@ -121,8 +121,7 @@ TEST(greasepencil, remove_drawing)
expected_frames_pairs_layer0[i][1]); expected_frames_pairs_layer0[i][1]);
} }
} }
layer_i++; }
});
} }
/* --------------------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------------------- */
@ -167,35 +166,23 @@ struct GreasePencilLayerTreeExample {
TEST(greasepencil, layer_tree_pre_order_iteration_callback) TEST(greasepencil, layer_tree_pre_order_iteration_callback)
{ {
GreasePencilLayerTreeExample ex; GreasePencilLayerTreeExample ex;
int i = 0; ex.root.foreach_children_with_index_pre_order(
ex.root.foreach_children_pre_order( [&](uint64_t i, TreeNode &child) { EXPECT_EQ(child.name, ex.names[i]); });
[&](TreeNode &child) { EXPECT_EQ(child.name, ex.names[i++]); });
}
TEST(greasepencil, layer_tree_pre_order_iteration_loop)
{
GreasePencilLayerTreeExample ex;
int i = 0;
for (TreeNode &child : ex.root.children_in_pre_order()) {
EXPECT_EQ(child.name, ex.names[i++]);
}
} }
TEST(greasepencil, layer_tree_total_size) TEST(greasepencil, layer_tree_total_size)
{ {
GreasePencilLayerTreeExample ex; GreasePencilLayerTreeExample ex;
EXPECT_EQ(ex.root.total_num_children(), 7); EXPECT_EQ(ex.root.num_children_total(), 7);
} }
TEST(greasepencil, layer_tree_node_types) TEST(greasepencil, layer_tree_node_types)
{ {
GreasePencilLayerTreeExample ex; GreasePencilLayerTreeExample ex;
int i = 0; ex.root.foreach_children_with_index_pre_order([&](uint64_t i, TreeNode &child) {
for (TreeNode &child : ex.root.children_in_pre_order()) {
EXPECT_EQ(child.is_layer(), ex.is_layer[i]); EXPECT_EQ(child.is_layer(), ex.is_layer[i]);
EXPECT_EQ(child.is_group(), !ex.is_layer[i]); EXPECT_EQ(child.is_group(), !ex.is_layer[i]);
i++; });
}
} }
} // namespace blender::bke::greasepencil::tests } // namespace blender::bke::greasepencil::tests

View File

@ -124,8 +124,9 @@ class ObjectModule {
} }
uint layer_offset = layers_.object_offset_get(); uint layer_offset = layers_.object_offset_get();
grease_pencil.root_group().foreach_layer_pre_order( for (const Layer *layer : grease_pencil.layers()) {
[&](Layer &layer) { layers_.sync(object, layer, do_layer_blending); }); layers_.sync(object, *layer, do_layer_blending);
}
/* Order rendering using camera Z distance. */ /* Order rendering using camera Z distance. */
float3 position = float3(object->object_to_world[3]); float3 position = float3(object->object_to_world[3]);

View File

@ -49,10 +49,10 @@ struct PaintOperationExecutor {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_eval->data); GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_eval->data);
if (!grease_pencil.runtime->has_active_layer()) { if (!grease_pencil.runtime->has_active_layer()) {
/* TODO: create a new layer. */ /* TODO: create a new layer. */
grease_pencil.runtime->set_active_layer(0); grease_pencil.runtime->set_active_layer_index(0);
} }
const bke::greasepencil::Layer &active_layer = grease_pencil.runtime->active_layer(); const bke::greasepencil::Layer &active_layer = grease_pencil.runtime->active_layer();
int index = active_layer.drawing_at(scene->r.cfra); int index = active_layer.drawing_index_at(scene->r.cfra);
BLI_assert(index != -1); BLI_assert(index != -1);
GreasePencilDrawing &drawing = *reinterpret_cast<GreasePencilDrawing *>( GreasePencilDrawing &drawing = *reinterpret_cast<GreasePencilDrawing *>(
@ -90,8 +90,8 @@ void PaintOperation::on_stroke_done(const bContext &C)
grease_pencil_eval.runtime->has_active_layer()); grease_pencil_eval.runtime->has_active_layer());
const bke::greasepencil::Layer &active_layer_orig = grease_pencil_orig.runtime->active_layer(); const bke::greasepencil::Layer &active_layer_orig = grease_pencil_orig.runtime->active_layer();
const bke::greasepencil::Layer &active_layer_eval = grease_pencil_eval.runtime->active_layer(); const bke::greasepencil::Layer &active_layer_eval = grease_pencil_eval.runtime->active_layer();
int index_orig = active_layer_orig.drawing_at(scene->r.cfra); int index_orig = active_layer_orig.drawing_index_at(scene->r.cfra);
int index_eval = active_layer_eval.drawing_at(scene->r.cfra); int index_eval = active_layer_eval.drawing_index_at(scene->r.cfra);
BLI_assert(index_orig != -1 && index_eval != -1); BLI_assert(index_orig != -1 && index_eval != -1);
GreasePencilDrawing &drawing_orig = *reinterpret_cast<GreasePencilDrawing *>( GreasePencilDrawing &drawing_orig = *reinterpret_cast<GreasePencilDrawing *>(

View File

@ -203,7 +203,6 @@ typedef struct GreasePencil {
GreasePencilDrawingOrReference **drawing_array; GreasePencilDrawingOrReference **drawing_array;
int drawing_array_size; int drawing_array_size;
char _pad[4]; char _pad[4];
#ifdef __cplusplus #ifdef __cplusplus
void read_drawing_array(BlendDataReader *reader); void read_drawing_array(BlendDataReader *reader);
void write_drawing_array(BlendWriter *writer); void write_drawing_array(BlendWriter *writer);
@ -241,7 +240,11 @@ typedef struct GreasePencil {
void remove_drawing(int index); void remove_drawing(int index);
void foreach_visible_drawing(int frame, void foreach_visible_drawing(int frame,
blender::FunctionRef<void(GreasePencilDrawing &)> function); blender::FunctionRef<void(GreasePencilDrawing &)> function);
blender::bke::greasepencil::LayerGroup &root_group(); const blender::bke::greasepencil::LayerGroup &root_group() const;
blender::bke::greasepencil::LayerGroup &root_group_for_write();
blender::Span<const blender::bke::greasepencil::Layer *> layers() const;
blender::Span<blender::bke::greasepencil::Layer *> layers_for_write();
void tag_layer_tree_changed();
#endif #endif
} GreasePencil; } GreasePencil;