Initial Grease Pencil 3.0 stage #106848
|
@ -7,6 +7,7 @@
|
||||||
* \brief Low-level operations for grease pencil.
|
* \brief Low-level operations for grease pencil.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "BLI_function_ref.hh"
|
||||||
#include "BLI_map.hh"
|
#include "BLI_map.hh"
|
||||||
#include "BLI_stack.hh"
|
#include "BLI_stack.hh"
|
||||||
#include "BLI_string.h"
|
#include "BLI_string.h"
|
||||||
|
@ -16,37 +17,43 @@
|
||||||
|
|
||||||
namespace blender::bke {
|
namespace blender::bke {
|
||||||
|
|
||||||
class GreasePencilLayerRuntime {
|
|
||||||
public:
|
|
||||||
Map<int, int> frames;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace gpencil {
|
namespace gpencil {
|
||||||
|
|
||||||
|
class LayerGroup;
|
||||||
|
class Layer;
|
||||||
filedescriptor marked this conversation as resolved
Outdated
|
|||||||
|
|
||||||
class TreeNode : public ::GreasePencilLayerTreeNode {
|
class TreeNode : public ::GreasePencilLayerTreeNode {
|
||||||
private:
|
using ItemIterFn = FunctionRef<void(TreeNode &)>;
|
||||||
|
|
||||||
|
protected:
|
||||||
Vector<TreeNode, 0> children_;
|
Vector<TreeNode, 0> children_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TreeNode()
|
TreeNode(GreasePencilLayerTreeNodeType type)
|
||||||
{
|
{
|
||||||
|
this->type = type;
|
||||||
this->name = nullptr;
|
this->name = nullptr;
|
||||||
}
|
}
|
||||||
TreeNode(StringRefNull name)
|
TreeNode(GreasePencilLayerTreeNodeType type, StringRefNull name)
|
||||||
{
|
{
|
||||||
|
this->type = type;
|
||||||
Hans Goudey
commented
Is there a particular reason you remove copy assignment but keep copy construction defined? Is there a particular reason you remove copy assignment but keep copy construction defined?
Falk David
commented
Just because copying should be explicit. And removing the copy assignment constructor avoids errors. Just because copying should be explicit. And removing the copy assignment constructor avoids errors.
|
|||||||
this->name = BLI_strdup(name.c_str());
|
this->name = BLI_strdup(name.c_str());
|
||||||
}
|
}
|
||||||
TreeNode(const TreeNode &other) : TreeNode(other.name)
|
TreeNode(const TreeNode &other)
|
||||||
{
|
{
|
||||||
|
this->name = BLI_strdup(other.name);
|
||||||
}
|
}
|
||||||
TreeNode(TreeNode &&other)
|
TreeNode(TreeNode &&other)
|
||||||
{
|
{
|
||||||
std::swap(this->name, other.name);
|
this->name = other.name;
|
||||||
other.name = nullptr;
|
other.name = nullptr;
|
||||||
}
|
}
|
||||||
Hans Goudey
commented
Is defining these as Is defining these as `constexpr` helpful? I sort of doubt any real computation is done on these nodes at compile time. But maybe?
Hans Goudey
commented
The The `const` in the `const bool` return type means nothing here
|
|||||||
TreeNode &operator=(const TreeNode &other)
|
TreeNode &operator=(const TreeNode &other)
|
||||||
{
|
{
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
|
if (this->name) {
|
||||||
|
MEM_freeN(this->name);
|
||||||
|
}
|
||||||
this->name = BLI_strdup(other.name);
|
this->name = BLI_strdup(other.name);
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -54,7 +61,7 @@ class TreeNode : public ::GreasePencilLayerTreeNode {
|
||||||
TreeNode &operator=(TreeNode &&other)
|
TreeNode &operator=(TreeNode &&other)
|
||||||
{
|
{
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
std::swap(this->name, other.name);
|
this->name = other.name;
|
||||||
other.name = nullptr;
|
other.name = nullptr;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -146,48 +153,174 @@ class TreeNode : public ::GreasePencilLayerTreeNode {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool operator==(const TreeNode &other) const
|
constexpr bool is_group() const
|
||||||
Hans Goudey
commented
Better to return a span here maybe? Better to return a span here maybe?
|
|||||||
{
|
{
|
||||||
return this == &other;
|
return type == GREASE_PENCIL_LAYER_TREE_GROUP;
|
||||||
}
|
|
||||||
bool operator!=(const TreeNode &other) const
|
|
||||||
{
|
|
||||||
return this != &other;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_child(TreeNode &node)
|
constexpr bool is_layer() const
|
||||||
{
|
{
|
||||||
children_.append(node);
|
return type == GREASE_PENCIL_LAYER_TREE_LEAF;
|
||||||
Falk David
commented
Put out of line. Put out of line.
|
|||||||
}
|
}
|
||||||
|
|
||||||
void add_child(TreeNode &&node)
|
LayerGroup &as_group()
|
||||||
Hans Goudey
commented
The function is "is_locked" and the comment says "return if it is locked". This sort of comment doesn't add anything IMO, just wastes space. If there is a comment, maybe it should use a different word besides "locked" to describe the state. Or there doesn't need to be a comment at all. The function is "is_locked" and the comment says "return if it is locked". This sort of comment doesn't add anything IMO, just wastes space. If there is a comment, maybe it should use a different word besides "locked" to describe the state. Or there doesn't need to be a comment at all.
|
|||||||
{
|
{
|
||||||
children_.append(std::move(node));
|
return *static_cast<LayerGroup *>(this);
|
||||||
}
|
}
|
||||||
|
Layer &as_layer()
|
||||||
int size()
|
|
||||||
{
|
{
|
||||||
return children_.size();
|
return *static_cast<Layer *>(this);
|
||||||
}
|
|
||||||
|
|
||||||
bool remove_child(TreeNode &node)
|
|
||||||
{
|
|
||||||
BLI_assert(children_.size() != 0);
|
|
||||||
int64_t index = children_.first_index_of_try(node);
|
|
||||||
if (index < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
children_.remove(index);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PreOrderRange children_in_pre_order()
|
PreOrderRange children_in_pre_order()
|
||||||
{
|
{
|
||||||
return PreOrderRange(this);
|
return PreOrderRange(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void foreach_children_pre_order(ItemIterFn function)
|
||||||
|
{
|
||||||
|
for (TreeNode &child : children_) {
|
||||||
|
child.foreach_children_pre_order_recursive_(function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void foreach_leaf_pre_order(ItemIterFn function)
|
||||||
|
{
|
||||||
|
for (TreeNode &child : children_) {
|
||||||
|
child.foreach_leaf_pre_order_recursive_(function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
filedescriptor marked this conversation as resolved
Outdated
Bastien Montagne
commented
Would be good to know why this is commented out, or it should be cleaned up before merge in main. Would be good to know why this is commented out, or it should be cleaned up before merge in main.
|
|||||||
|
private:
|
||||||
|
void foreach_children_pre_order_recursive_(ItemIterFn function)
|
||||||
|
{
|
||||||
|
function(*this);
|
||||||
|
for (TreeNode &child : children_) {
|
||||||
|
child.foreach_children_pre_order_recursive_(function);
|
||||||
|
}
|
||||||
Falk David
commented
Put out of line. Put out of line.
|
|||||||
|
}
|
||||||
|
|
||||||
|
void foreach_leaf_pre_order_recursive_(ItemIterFn function)
|
||||||
|
{
|
||||||
|
if (children_.size() == 0) {
|
||||||
|
function(*this);
|
||||||
|
}
|
||||||
|
for (TreeNode &child : children_) {
|
||||||
Falk David
commented
Put out of line. Put out of line.
|
|||||||
|
child.foreach_children_pre_order_recursive_(function);
|
||||||
|
}
|
||||||
Bastien Montagne
commented
Not sure what is a 'pre-order vector'? or is a typo? Like Not sure what is a 'pre-order vector'? or is a typo? Like `pre-ordered vector`? Same below.
|
|||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Layer : TreeNode, ::GreasePencilLayer {};
|
class Layer : public TreeNode, ::GreasePencilLayer {
|
||||||
|
private:
|
||||||
|
Map<int, int> frames_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Layer() : TreeNode(GREASE_PENCIL_LAYER_TREE_LEAF), frames_()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Layer(StringRefNull name) : TreeNode(GREASE_PENCIL_LAYER_TREE_LEAF, name), frames_()
|
||||||
Hans Goudey
commented
Returning an element added to a vector by reference is quite dangerous since they're invalidated by further additions, we usually don't do that. Either returning an index or not having these wrapper functions seems like the best choices IMO Returning an element added to a vector by reference is quite dangerous since they're invalidated by further additions, we usually don't do that. Either returning an index or not having these wrapper functions seems like the best choices IMO
Sergey Sharybin
commented
From quick look it seems that it should actually be From quick look it seems that it should actually be `emplace_layer()` or something like this.
And for that it is typically very handy to return reference. And not only that, since C++17 it is expected behavior for vector type containers as well.
Falk David
commented
I think I think `emplace_layer` would be a better name indeed.
|
|||||||
|
{
|
||||||
|
}
|
||||||
|
Layer(const Layer &other)
|
||||||
|
: TreeNode(GREASE_PENCIL_LAYER_TREE_LEAF, other.name), frames_(other.frames_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Layer(Layer &&other) : TreeNode(std::move(other))
|
||||||
|
{
|
||||||
|
frames_ = std::move(other.frames_);
|
||||||
|
}
|
||||||
|
Layer &operator=(const Layer &other)
|
||||||
|
{
|
||||||
|
return copy_assign_container(*this, other);
|
||||||
|
}
|
||||||
|
Layer &operator=(Layer &&other)
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
frames_ = std::move(other.frames_);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Layer &other) const
|
||||||
|
{
|
||||||
|
return this == &other;
|
||||||
|
}
|
||||||
|
bool operator!=(const Layer &other) const
|
||||||
filedescriptor marked this conversation as resolved
Outdated
Falk David
commented
Since the map will have to change to store more than just an index as the value, make sure to update the comment too. Since the map will have to change to store more than just an index as the value, make sure to update the comment too.
|
|||||||
|
{
|
||||||
|
return this != &other;
|
||||||
|
}
|
||||||
|
|
||||||
|
Layer &as_layer()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LayerGroup : public TreeNode {
|
||||||
|
public:
|
||||||
|
LayerGroup() : TreeNode(GREASE_PENCIL_LAYER_TREE_GROUP)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
LayerGroup(StringRefNull name) : TreeNode(GREASE_PENCIL_LAYER_TREE_GROUP, name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
LayerGroup(const LayerGroup &other) : TreeNode(GREASE_PENCIL_LAYER_TREE_GROUP, other.name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
LayerGroup(LayerGroup &&other) : TreeNode(std::move(other))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~LayerGroup()
|
||||||
|
{
|
||||||
|
}
|
||||||
Bastien Montagne
commented
Not sure why the Not sure why the `greasepencil` namespace ends up here? Also means that e.g. the fairly generically-named `StrokePoint` struct is in the fairly generic `blender::bke` namespace, which does not sounds great to me? not to mention 'C-style' namespace in names like `GreasePencilDrawingRuntime`.
|
|||||||
|
|
||||||
|
public:
|
||||||
|
bool operator==(const LayerGroup &other) const
|
||||||
|
{
|
||||||
|
return this == &other;
|
||||||
|
}
|
||||||
|
bool operator!=(const LayerGroup &other) const
|
||||||
|
{
|
||||||
|
return this != &other;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_group(LayerGroup &group)
|
||||||
|
{
|
||||||
|
children_.append(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_group(LayerGroup &&group)
|
||||||
|
{
|
||||||
|
children_.append(std::move(group));
|
||||||
|
}
|
||||||
Hans Goudey
commented
`= {}` doesn't change anything here since `Vector` has a default constructor. It can be removed I think. Same with below
|
|||||||
|
|
||||||
Falk David
commented
Put out of line. Put out of line.
|
|||||||
|
void add_layer(Layer &layer)
|
||||||
|
{
|
||||||
|
children_.append(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_layer(Layer &&layer)
|
||||||
Hans Goudey
commented
Looks like this should be private maybe? It has a Looks like this should be private maybe? It has a `_` suffix.
|
|||||||
|
{
|
||||||
|
children_.append(std::move(layer));
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_children()
|
||||||
|
{
|
||||||
|
return children_.size();
|
||||||
Falk David
commented
Put out of line. Put out of line.
|
|||||||
|
}
|
||||||
|
|
||||||
|
void remove_child(int64_t index)
|
||||||
|
{
|
||||||
|
BLI_assert(index >= 0 && index < children_.size());
|
||||||
|
children_.remove(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LayerTree {
|
||||||
Falk David
commented
Put out of line. Put out of line.
|
|||||||
|
private:
|
||||||
|
LayerGroup root_;
|
||||||
|
};
|
||||||
filedescriptor marked this conversation as resolved
Outdated
Bastien Montagne
commented
Same remark as above about commented out code. Same remark as above about commented out code.
|
|||||||
|
|
||||||
namespace convert {
|
namespace convert {
|
||||||
|
|
||||||
Hans Goudey
commented
These two functions should never have to be called outside of the These two functions should never have to be called outside of the `ID` callbacks in `grease_pencil.cc`; IMO they make more sense as static functions there. Probably better to keep that storage for DNA thing as localized as possible.
|
|||||||
|
@ -199,6 +332,11 @@ void legacy_gpencil_to_grease_pencil(bGPdata &gpd, GreasePencil &grease_pencil);
|
||||||
|
|
||||||
} // namespace gpencil
|
} // namespace gpencil
|
||||||
|
|
||||||
|
class GreasePencilRuntime {
|
||||||
|
private:
|
||||||
|
gpencil::LayerTree layer_tree_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace blender::bke
|
} // namespace blender::bke
|
||||||
|
|
||||||
struct Main;
|
struct Main;
|
||||||
|
@ -206,8 +344,3 @@ struct BoundBox;
|
||||||
|
|
||||||
void *BKE_grease_pencil_add(Main *bmain, const char *name);
|
void *BKE_grease_pencil_add(Main *bmain, const char *name);
|
||||||
BoundBox *BKE_grease_pencil_boundbox_get(Object *ob);
|
BoundBox *BKE_grease_pencil_boundbox_get(Object *ob);
|
||||||
|
|
||||||
inline const blender::Map<int, int> &GreasePencilLayer::frames() const
|
|
||||||
{
|
|
||||||
return this->runtime->frames;
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,18 +11,22 @@ namespace blender::bke::gpencil::tests {
|
||||||
|
|
||||||
TEST(gpencil, build_layer_tree)
|
TEST(gpencil, build_layer_tree)
|
||||||
{
|
{
|
||||||
TreeNode root{};
|
LayerGroup root{};
|
||||||
|
|
||||||
TreeNode node("Node1");
|
|
||||||
node.add_child(TreeNode("Child1"));
|
|
||||||
node.add_child(TreeNode("Child2"));
|
|
||||||
|
|
||||||
root.add_child(std::move(node));
|
|
||||||
root.add_child(TreeNode("Node2"));
|
|
||||||
|
|
||||||
for (TreeNode &node : root.children_in_pre_order()) {
|
LayerGroup group("Group1");
|
||||||
std::cout << node.name << std::endl;
|
group.add_layer(Layer("Child1"));
|
||||||
}
|
group.add_layer(Layer("Child2"));
|
||||||
|
|
||||||
Sergey Sharybin
commented
Use text fixtures: http://google.github.io/googletest/primer.html#same-data-multiple-tests Use text fixtures: http://google.github.io/googletest/primer.html#same-data-multiple-tests
Falk David
commented
I used fixtures before, but decided to use this approach instead, because all the tests are then under the same I used fixtures before, but decided to use this approach instead, because all the tests are then under the same `greasepencil` namespace. I didn't find a way to do this with fixtures, unless I name the class literally `greasepencil`.
Sergey Sharybin
commented
You can't do that either. It is at a very least discouraged to use the same name for fixture and "regular" test. Typically the "namespace" defines context you're testing, and it is perfectly fine to have multiple of such contexts per file ( I would love to improve the monolithic state of tests at some point (as it is really in a way every time I work on test), but that is outside of the scope of this patch. Would be nice to add a comment on top of the You can't do that either. It is at a very least discouraged to use the same name for fixture and "regular" test.
Typically the "namespace" defines context you're testing, and it is perfectly fine to have multiple of such contexts per file (`euclidean_resection_test.cc`). But with the monolithic nature of BKE/BLI tests there is no good way to achieve the same behavior.
I would love to improve the monolithic state of tests at some point (as it is really in a way every time I work on test), but that is outside of the scope of this patch.
Would be nice to add a comment on top of the `GreasePencilIDTestContext` which briefly summarizes your choice of this approach. Basically, so if someone else stumbles on this code and winders "why not fixtures" they have an answer.
|
|||||||
|
root.add_group(std::move(group));
|
||||||
|
root.add_layer(Layer("Group2"));
|
||||||
|
|
||||||
|
root.foreach_children_pre_order([](LayerGroup &child) { std::cout << child.name << std::endl; });
|
||||||
|
|
||||||
|
// root.remove_child(0);
|
||||||
|
|
||||||
|
// for (LayerGroup &child : root.children_in_pre_order()) {
|
||||||
|
// std::cout << child.name << std::endl;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace blender::bke::gpencil::tests
|
} // namespace blender::bke::gpencil::tests
|
|
@ -13,10 +13,8 @@
|
||||||
# include "BLI_map.hh"
|
# include "BLI_map.hh"
|
||||||
# include "BLI_span.hh"
|
# include "BLI_span.hh"
|
||||||
namespace blender::bke {
|
namespace blender::bke {
|
||||||
class GreasePencilLayerRuntime;
|
|
||||||
class GreasePencilRuntime;
|
class GreasePencilRuntime;
|
||||||
} // namespace blender::bke
|
} // namespace blender::bke
|
||||||
using GreasePencilLayerRuntimeHandle = blender::bke::GreasePencilLayerRuntime;
|
|
||||||
using GreasePencilRuntimeHandle = blender::bke::GreasePencilRuntime;
|
using GreasePencilRuntimeHandle = blender::bke::GreasePencilRuntime;
|
||||||
#else
|
#else
|
||||||
typedef struct GreasePencilLayerRuntimeHandle GreasePencilLayerRuntimeHandle;
|
typedef struct GreasePencilLayerRuntimeHandle GreasePencilLayerRuntimeHandle;
|
||||||
|
@ -69,7 +67,30 @@ typedef struct GreasePencilDrawingReference {
|
||||||
struct GreasePencil *id_reference;
|
struct GreasePencil *id_reference;
|
||||||
} GreasePencilDrawingReference;
|
} GreasePencilDrawingReference;
|
||||||
|
|
||||||
filedescriptor marked this conversation as resolved
Outdated
Falk David
commented
Move below runtime data pointer. Move below runtime data pointer.
|
|||||||
/* Only used for storage in the .blend file. */
|
/**
|
||||||
|
* This Map maps a scene frame number (key) to an index into GreasePencil->drawings (value). The
|
||||||
|
* frame number indicates the first frame the drawing is shown. The end time is implicitly
|
||||||
|
* defined by the next greater frame number (key) in the map. If the value mapped to (index) is
|
||||||
|
* -1, no drawing is shown at this frame.
|
||||||
|
*
|
||||||
|
* \example:
|
||||||
|
*
|
||||||
|
* {0: 0, 5: 1, 10: -1, 12: 2, 16: -1}
|
||||||
|
*
|
||||||
|
* In this example there are three drawings (drawing #0, drawing #1 and drawing #2). The first
|
||||||
|
* drawing starts at frame 0 and ends at frame 5 (excusive). The second drawing starts at
|
||||||
|
* frame 5 and ends at frame 10. Finally, the third drawing starts at frame 12 and ends at
|
||||||
|
* frame 16.
|
||||||
|
*
|
||||||
|
* | | | | | | | | | | |1|1|1|1|1|1|1|
|
||||||
Hans Goudey
commented
I wonder if this user count could be runtime data? Or if not, maybe it's worth mentioning why it isn't in a comment. I wonder if this user count could be runtime data? Or if not, maybe it's worth mentioning why it isn't in a comment.
Falk David
commented
I thought about this, but I don't think it can be runtime data. Since there will be keyframe instances, we need to make the user count is saved. I thought about this, but I don't think it can be runtime data. Since there will be keyframe instances, we need to make the user count is saved.
Hans Goudey
commented
The connection between keyframe instances and saving the user count isn't super clear to me. Maybe a comment about what the user count is used for here would help? The connection between keyframe instances and saving the user count isn't super clear to me. Maybe a comment about what the user count is used for here would help?
Brecht Van Lommel
commented
Maybe it's still better to have this as runtime data. If there is ever a bug where this doesn't get counted correctly, at least a file read could fix it. It should be possible to read this as zero, and increment it for every user? This is easy to change afterwards though. Maybe it's still better to have this as runtime data. If there is ever a bug where this doesn't get counted correctly, at least a file read could fix it. It should be possible to read this as zero, and increment it for every user?
This is easy to change afterwards though.
Falk David
commented
I see, re-creating the user count when reading could work indeed. I see, re-creating the user count when reading could work indeed.
|
|||||||
|
* Time: |0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|...
|
||||||
|
* Frame: [#0 ][#1 ] [#2 ]
|
||||||
|
*
|
||||||
|
* \note If a drawing references another data-block, all of the drawings in that data-block are
|
||||||
|
* mapped sequentially to the frames (frame-by-frame). If another frame starts, the rest of the
|
||||||
|
* referenced drawings are discarded. If the frame is longer than the number of referenced
|
||||||
|
* drawings, then the last referenced drawing is held for the rest of the duration.
|
||||||
|
*/
|
||||||
typedef struct GreasePencilLayerFramesMapStorage {
|
typedef struct GreasePencilLayerFramesMapStorage {
|
||||||
/* Array of `frames` keys (sorted in ascending order). */
|
/* Array of `frames` keys (sorted in ascending order). */
|
||||||
int *keys;
|
int *keys;
|
||||||
|
@ -84,39 +105,8 @@ typedef struct GreasePencilLayerFramesMapStorage {
|
||||||
* A grease pencil layer is a collection of drawings mapped to a specific time on the timeline.
|
* A grease pencil layer is a collection of drawings mapped to a specific time on the timeline.
|
||||||
*/
|
*/
|
||||||
typedef struct GreasePencilLayer {
|
typedef struct GreasePencilLayer {
|
||||||
#ifdef __cplusplus
|
/* Only used for storage in the .blend file. */
|
||||||
/**
|
|
||||||
* This Map maps a scene frame number (key) to an index into GreasePencil->drawings (value). The
|
|
||||||
* frame number indicates the first frame the drawing is shown. The end time is implicitly
|
|
||||||
* defined by the next greater frame number (key) in the map. If the value mapped to (index) is
|
|
||||||
* -1, no drawing is shown at this frame.
|
|
||||||
*
|
|
||||||
* \example:
|
|
||||||
*
|
|
||||||
* {0: 0, 5: 1, 10: -1, 12: 2, 16: -1}
|
|
||||||
*
|
|
||||||
* In this example there are three drawings (drawing #0, drawing #1 and drawing #2). The first
|
|
||||||
* drawing starts at frame 0 and ends at frame 5 (excusive). The second drawing starts at
|
|
||||||
* frame 5 and ends at frame 10. Finally, the third drawing starts at frame 12 and ends at
|
|
||||||
* frame 16.
|
|
||||||
*
|
|
||||||
* | | | | | | | | | | |1|1|1|1|1|1|1|
|
|
||||||
* Time: |0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|...
|
|
||||||
* Frame: [#0 ][#1 ] [#2 ]
|
|
||||||
*
|
|
||||||
* \note If a drawing references another data-block, all of the drawings in that data-block are
|
|
||||||
* mapped sequentially to the frames (frame-by-frame). If another frame starts, the rest of the
|
|
||||||
* referenced drawings are discarded. If the frame is longer than the number of referenced
|
|
||||||
* drawings, then the last referenced drawing is held for the rest of the duration.
|
|
||||||
*/
|
|
||||||
const blender::Map<int, int> &frames() const;
|
|
||||||
#endif
|
|
||||||
GreasePencilLayerFramesMapStorage frames_storage;
|
GreasePencilLayerFramesMapStorage frames_storage;
|
||||||
|
|
||||||
/**
|
|
||||||
* Runtime struct pointer.
|
|
||||||
*/
|
|
||||||
GreasePencilLayerRuntimeHandle *runtime;
|
|
||||||
} GreasePencilLayer;
|
} GreasePencilLayer;
|
||||||
|
|
||||||
typedef enum GreasePencilLayerTreeNodeType {
|
typedef enum GreasePencilLayerTreeNodeType {
|
||||||
Hans Goudey
commented
What about a single function that returned What about a single function that returned `std::optional` rather than a separate "has_" function?
|
|||||||
|
|
Decide on the namespace here. Should it maybe be just
gp
?