GPv3: Layer Parenting/Transforms #117247

Merged
Falk David merged 50 commits from filedescriptor/blender:gpv3-layer-parenting into main 2024-02-07 16:28:24 +01:00
23 changed files with 452 additions and 126 deletions

View File

@ -15,6 +15,17 @@ class DataButtonsPanel:
return hasattr(context, "grease_pencil") and context.grease_pencil
class LayerDataButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
@classmethod
def poll(cls, context):
grease_pencil = context.grease_pencil
return grease_pencil and grease_pencil.layers.active
class DATA_PT_context_grease_pencil(DataButtonsPanel, Panel):
bl_label = ""
bl_options = {'HIDE_HEADER'}
@ -67,13 +78,59 @@ class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel):
layout.use_property_decorate = True
col = layout.column(align=True)
col = layout.row(align=True)
col.prop(layer, "opacity", text="Opacity", slider=True)
row = layout.row(align=True)
row.prop(layer, "opacity", text="Opacity", slider=True)
class DATA_PT_grease_pencil_layer_transform(LayerDataButtonsPanel, Panel):
bl_label = "Transform"
bl_parent_id = "DATA_PT_grease_pencil_layers"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
layout.active = not layer.lock
row = layout.row(align=True)
row.prop(layer, "translation")
row = layout.row(align=True)
row.prop(layer, "rotation")
row = layout.row(align=True)
row.prop(layer, "scale")
class DATA_PT_grease_pencil_layer_relations(LayerDataButtonsPanel, Panel):
bl_label = "Relations"
bl_parent_id = "DATA_PT_grease_pencil_layers"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
grease_pencil = context.grease_pencil
layer = grease_pencil.layers.active
layout.active = not layer.lock
row = layout.row(align=True)
row.prop(layer, "parent", text="Parent")
if layer.parent and layer.parent.type == 'ARMATURE':
row = layout.row(align=True)
row.prop_search(layer, "parent_bone", layer.parent.data, "bones", text="Bone")
classes = (
DATA_PT_context_grease_pencil,
DATA_PT_grease_pencil_layers,
DATA_PT_grease_pencil_layer_transform,
DATA_PT_grease_pencil_layer_relations,
GREASE_PENCIL_MT_grease_pencil_add_layer_extra,
)

View File

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

View File

@ -34,14 +34,6 @@ namespace blender::bke {
namespace greasepencil {
struct DrawingTransforms {
float4x4 world_space_to_layer_space;
float4x4 layer_space_to_world_space;
DrawingTransforms() = default;
DrawingTransforms(const Object &grease_pencil_ob);
};
class DrawingRuntime {
public:
/**
@ -447,6 +439,23 @@ class Layer : public ::GreasePencilLayer {
*/
void update_from_dna_read();
/**
* Returns the transformation from layer space to object space.
*/
float4x4 to_object_space(const Object &object) const;
/**
* Returns the transformation from layer space to world space.
*/
float4x4 to_world_space(const Object &object) const;
/**
* Returns the name of the parent bone. Should only be used in case the parent object is an
* armature.
*/
StringRefNull parent_bone_name() const;
void set_parent_bone_name(const char *new_name);
private:
using SortedKeysIterator = const int *;
@ -460,6 +469,16 @@ class Layer : public ::GreasePencilLayer {
*/
SortedKeysIterator remove_leading_null_frames_in_range(SortedKeysIterator begin,
SortedKeysIterator end);
/**
* The local transform of the layer (in layer space, not object space).
*/
float4x4 local_transform() const;
/**
* Get the parent to world marix for this layer.
*/
float4x4 parent_to_world(const Object &parent) const;
};
class LayerGroupRuntime {

View File

@ -8,6 +8,7 @@
#include <iostream>
#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_curves.hh"
#include "BKE_customdata.hh"
@ -25,8 +26,10 @@
#include "BLI_bounds.hh"
#include "BLI_map.hh"
#include "BLI_math_euler_types.hh"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_memarena.h"
#include "BLI_memory_utils.hh"
@ -240,14 +243,6 @@ IDTypeInfo IDType_ID_GP = {
namespace blender::bke::greasepencil {
filedescriptor marked this conversation as resolved Outdated

Are these changes still necessary? Seems this isn't used

Are these changes still necessary? Seems this isn't used
DrawingTransforms::DrawingTransforms(const Object &grease_pencil_ob)
{
/* TODO: For now layer space = object space. This needs to change once the layers have a
* transform. */
this->layer_space_to_world_space = float4x4_view(grease_pencil_ob.object_to_world);
this->world_space_to_layer_space = math::invert(this->layer_space_to_world_space);
}
static const std::string ATTR_RADIUS = "radius";
static const std::string ATTR_OPACITY = "opacity";
static const std::string ATTR_VERTEX_COLOR = "vertex_color";
@ -664,6 +659,13 @@ Layer::Layer()
this->opacity = 1.0f;
this->parent = nullptr;
this->parsubstr = nullptr;
zero_v3(this->translation);
zero_v3(this->rotation);
copy_v3_fl(this->scale, 1.0f);
BLI_listbase_clear(&this->masks);
this->runtime = MEM_new<LayerRuntime>(__func__);
@ -680,11 +682,18 @@ Layer::Layer(const Layer &other) : Layer()
/* TODO: duplicate masks. */
/* Note: We do not duplicate the frame storage since it is only needed for writing. */
/* Note: We do not duplicate the frame storage since it is only needed for writing to file. */
this->blend_mode = other.blend_mode;
this->opacity = other.opacity;
this->parent = other.parent;
this->set_parent_bone_name(other.parsubstr);
copy_v3_v3(this->translation, other.translation);
copy_v3_v3(this->rotation, other.rotation);
copy_v3_v3(this->scale, other.scale);
this->runtime->frames_ = other.runtime->frames_;
this->runtime->sorted_keys_cache_ = other.runtime->sorted_keys_cache_;
/* TODO: what about masks cache? */
@ -702,6 +711,8 @@ Layer::~Layer()
MEM_freeN(mask);
}
MEM_SAFE_FREE(this->parsubstr);
MEM_delete(this->runtime);
this->runtime = nullptr;
}
@ -946,6 +957,57 @@ void Layer::update_from_dna_read()
}
}
float4x4 Layer::to_world_space(const Object &object) const
{
if (this->parent == nullptr) {
return float4x4(object.object_to_world) * this->local_transform();
}
const Object &parent = *this->parent;
return this->parent_to_world(parent) * this->local_transform();
}
float4x4 Layer::to_object_space(const Object &object) const
{
if (this->parent == nullptr) {
return this->local_transform();
}
const Object &parent = *this->parent;
return float4x4(object.world_to_object) * this->parent_to_world(parent) *
this->local_transform();
}
StringRefNull Layer::parent_bone_name() const
{
return (this->parsubstr != nullptr) ? StringRefNull(this->parsubstr) : StringRefNull();
}
void Layer::set_parent_bone_name(const char *new_name)
{
if (this->parsubstr != nullptr) {
MEM_freeN(this->parsubstr);
}
this->parsubstr = BLI_strdup_null(new_name);
}
float4x4 Layer::parent_to_world(const Object &parent) const
{
const float4x4 parent_object_to_world(parent.object_to_world);
if (parent.type == OB_ARMATURE && !this->parent_bone_name().is_empty()) {
if (bPoseChannel *channel = BKE_pose_channel_find_name(parent.pose,
this->parent_bone_name().c_str()))
{
return parent_object_to_world * float4x4_view(channel->pose_mat);
}
}
return parent_object_to_world;
}
float4x4 Layer::local_transform() const
{
return math::from_loc_rot_scale<float4x4, math::EulerXYZ>(
float3(this->translation), float3(this->rotation), float3(this->scale));
}
LayerGroup::LayerGroup()
{
new (&this->base) TreeNode(GP_LAYER_TREE_GROUP);

View File

@ -282,6 +282,13 @@ void legacy_gpencil_to_grease_pencil(Main &bmain, GreasePencil &grease_pencil, b
new_layer.blend_mode = int8_t(gpl->blend_mode);
new_layer.parent = gpl->parent;
new_layer.set_parent_bone_name(gpl->parsubstr);
copy_v3_v3(new_layer.translation, gpl->location);
copy_v3_v3(new_layer.rotation, gpl->rotation);
copy_v3_v3(new_layer.scale, gpl->scale);
/* Convert the layer masks. */
LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) {
LayerMask *new_mask = new LayerMask(mask->name);

View File

@ -2887,6 +2887,16 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 1)) {
using namespace blender::bke::greasepencil;
/* Initialize newly added scale layer transform to one. */
LISTBASE_FOREACH (GreasePencil *, grease_pencil, &bmain->grease_pencils) {
for (Layer *layer : grease_pencil->layers_for_write()) {
copy_v3_fl(layer->scale, 1.0f);
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@ -973,9 +973,11 @@ void DepsgraphNodeBuilder::build_object_data(Object *object)
case OB_CURVES:
case OB_POINTCLOUD:
case OB_VOLUME:
case OB_GREASE_PENCIL:
build_object_data_geometry(object);
break;
case OB_GREASE_PENCIL:
build_object_data_grease_pencil(object);
break;
case OB_ARMATURE:
build_rig(object);
break;
@ -1026,6 +1028,20 @@ void DepsgraphNodeBuilder::build_object_data_lightprobe(Object *object)
add_operation_node(&object->id, NodeType::PARAMETERS, OperationCode::LIGHT_PROBE_EVAL);
}
void DepsgraphNodeBuilder::build_object_data_grease_pencil(Object *object)
{
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
/* Build the layer parents. */
for (const bke::greasepencil::Layer *layer : grease_pencil.layers()) {
Object *parent = layer->parent;
if (parent == nullptr) {
continue;
}
build_object(-1, parent, DEG_ID_LINKED_INDIRECTLY, false);
}
build_object_data_geometry(object);
}
void DepsgraphNodeBuilder::build_object_data_speaker(Object *object)
{
Speaker *speaker = (Speaker *)object->data;

View File

@ -194,6 +194,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_object_data_light(Object *object);
virtual void build_object_data_lightprobe(Object *object);
virtual void build_object_data_speaker(Object *object);
virtual void build_object_data_grease_pencil(Object *object);
virtual void build_object_transform(Object *object);
virtual void build_object_constraints(Object *object);
virtual void build_object_pointcache(Object *object);

View File

@ -65,6 +65,7 @@
#include "BKE_effect.h"
#include "BKE_fcurve_driver.h"
#include "BKE_gpencil_modifier_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_idprop.h"
#include "BKE_image.h"
#include "BKE_key.hh"
@ -2728,9 +2729,32 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
break;
}
case ID_GP: {
GreasePencil &grease_pencil = *reinterpret_cast<GreasePencil *>(obdata);
/* Update geometry when time is changed. */
TimeSourceKey time_key;
ComponentKey geometry_key(obdata, NodeType::GEOMETRY);
ComponentKey geometry_key(&grease_pencil.id, NodeType::GEOMETRY);
add_relation(time_key, geometry_key, "Grease Pencil Frame Change");
/* Add relations for layer parents. */
for (const bke::greasepencil::Layer *layer : grease_pencil.layers()) {
Object *parent = layer->parent;
if (parent == nullptr) {
continue;
}
if (parent->type == OB_ARMATURE && !layer->parent_bone_name().is_empty()) {
ComponentKey bone_key(&parent->id, NodeType::BONE, layer->parent_bone_name().c_str());
OperationKey armature_key(
&parent->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL);
add_relation(bone_key, geometry_key, "Grease Pencil Layer Bone Parent");
add_relation(armature_key, geometry_key, "Grease Pencil Layer Armature Parent");
}
else {
ComponentKey transform_key(&parent->id, NodeType::TRANSFORM);
add_relation(transform_key, geometry_key, "Grease Pencil Layer Object Parent");
}
}
break;
}
default:

View File

@ -199,6 +199,16 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har
return packed;
}
static void copy_transformed_positions(const Span<float3> src_positions,
const IndexRange range,
const float4x4 &transform,
MutableSpan<float3> dst_positions)
{
for (const int point_i : range) {
dst_positions[point_i] = math::transform_point(transform, src_positions[point_i]);
}
}
static void grease_pencil_edit_batch_ensure(Object &object,
const GreasePencil &grease_pencil,
const Scene &scene)
@ -259,7 +269,8 @@ static void grease_pencil_edit_batch_ensure(Object &object,
int total_line_ids_num = 0;
int drawing_start_offset = 0;
for (const ed::greasepencil::DrawingInfo &info : drawings) {
const Layer *layer = layers[info.layer_index];
const Layer &layer = *layers[info.layer_index];
const float4x4 layer_space_to_object_space = layer.to_object_space(object);
const bke::CurvesGeometry &curves = info.drawing.strokes();
const bke::AttributeAccessor attributes = curves.attributes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
@ -272,11 +283,15 @@ static void grease_pencil_edit_batch_ensure(Object &object,
const VArray<float> selection_float = *attributes.lookup_or_default<float>(
".selection", bke::AttrDomain::Point, true);
edit_points.slice(drawing_start_offset, curves.points_num()).copy_from(curves.positions());
MutableSpan<float> selection_slice = edit_points_selection.slice(drawing_start_offset,
curves.points_num());
const IndexRange points(drawing_start_offset, curves.points_num());
const Span<float3> positions = curves.positions();
MutableSpan<float3> positions_slice = edit_points.slice(points);
threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange range) {
copy_transformed_positions(positions, range, layer_space_to_object_space, positions_slice);
});
MutableSpan<float> selection_slice = edit_points_selection.slice(points);
/* Do not show selection for locked layers. */
if (layer->is_locked()) {
if (layer.is_locked()) {
selection_slice.fill(0.0f);
}
else {
@ -296,7 +311,7 @@ static void grease_pencil_edit_batch_ensure(Object &object,
total_line_ids_num += array_utils::count_booleans(curves.cyclic(), editable_strokes);
/* Do not show points for locked layers. */
if (layer->is_locked()) {
if (layer.is_locked()) {
continue;
}
@ -493,6 +508,8 @@ static void grease_pencil_geom_batch_ensure(Object &object,
/* Fill buffers with data. */
for (const int drawing_i : drawings.index_range()) {
const ed::greasepencil::DrawingInfo &info = drawings[drawing_i];
const Layer &layer = *grease_pencil.layers()[info.layer_index];
const float4x4 layer_space_to_object_space = layer.to_object_space(object);
const bke::CurvesGeometry &curves = info.drawing.strokes();
const bke::AttributeAccessor attributes = curves.attributes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
@ -538,7 +555,8 @@ static void grease_pencil_geom_batch_ensure(Object &object,
float length,
GreasePencilStrokeVert &s_vert,
GreasePencilColorVert &c_vert) {
copy_v3_v3(s_vert.pos, positions[point_i]);
copy_v3_v3(s_vert.pos,
math::transform_point(layer_space_to_object_space, positions[point_i]));
s_vert.radius = radii[point_i] * ((end_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
s_vert.opacity = opacities[point_i] *
((start_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);

View File

@ -34,6 +34,7 @@
#include "BKE_context.hh"
#include "BKE_deform.hh"
#include "BKE_gpencil_modifier_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_layer.hh"
#include "BKE_main.hh"
#include "BKE_modifier.hh"
@ -336,6 +337,22 @@ void ED_armature_bone_rename(Main *bmain,
}
}
}
if (ob->type == OB_GREASE_PENCIL) {
using namespace blender;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
Object *parent = layer->parent;
if (parent == nullptr) {
continue;
}
StringRefNull bone_name = layer->parent_bone_name();
if (!bone_name.is_empty() && bone_name == StringRef(oldname)) {
layer->set_parent_bone_name(newname);
}
}
}
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}

View File

@ -525,16 +525,13 @@ void apply_selection_operation_at_index(GMutableSpan selection,
static std::optional<FindClosestData> find_closest_point_to_screen_co(
const ARegion *region,
const RegionView3D *rv3d,
const Object &object,
const Span<float3> positions,
const float4x4 &projection,
const IndexMask &points_mask,
const float2 mouse_pos,
float radius,
const FindClosestData &initial_closest)
{
const float4x4 projection = ED_view3d_ob_project_mat_get(rv3d, &object);
const float radius_sq = pow2f(radius);
const FindClosestData new_closest_data = threading::parallel_reduce(
points_mask.index_range(),
@ -581,17 +578,14 @@ static std::optional<FindClosestData> find_closest_point_to_screen_co(
static std::optional<FindClosestData> find_closest_curve_to_screen_co(
const ARegion *region,
const RegionView3D *rv3d,
const Object &object,
const OffsetIndices<int> points_by_curve,
const Span<float3> positions,
const float4x4 &projection,
const IndexMask &curves_mask,
const float2 mouse_pos,
float radius,
const FindClosestData &initial_closest)
{
const float4x4 projection = ED_view3d_ob_project_mat_get(rv3d, &object);
const float radius_sq = pow2f(radius);
const FindClosestData new_closest_data = threading::parallel_reduce(
@ -667,9 +661,9 @@ static std::optional<FindClosestData> find_closest_curve_to_screen_co(
std::optional<FindClosestData> closest_elem_find_screen_space(
const ViewContext &vc,
const Object &object,
const OffsetIndices<int> points_by_curve,
const Span<float3> positions,
const float4x4 &projection,
const IndexMask &mask,
const bke::AttrDomain domain,
const int2 coord,
@ -678,19 +672,17 @@ std::optional<FindClosestData> closest_elem_find_screen_space(
switch (domain) {
case bke::AttrDomain::Point:
return find_closest_point_to_screen_co(vc.region,
vc.rv3d,
object,
positions,
projection,
mask,
float2(coord),
ED_view3d_select_dist_px(),
initial_closest);
case bke::AttrDomain::Curve:
return find_closest_curve_to_screen_co(vc.region,
vc.rv3d,
object,
points_by_curve,
positions,
projection,
mask,
float2(coord),
ED_view3d_select_dist_px(),
@ -704,6 +696,7 @@ std::optional<FindClosestData> closest_elem_find_screen_space(
bool select_box(const ViewContext &vc,
bke::CurvesGeometry &curves,
const Span<float3> positions,
const float4x4 &projection,
const IndexMask &mask,
const bke::AttrDomain selection_domain,
const rcti &rect,
@ -718,8 +711,6 @@ bool select_box(const ViewContext &vc,
changed = true;
}
const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obact);
const OffsetIndices points_by_curve = curves.points_by_curve();
if (selection_domain == bke::AttrDomain::Point) {
mask.foreach_index(GrainSize(1024), [&](const int point_i) {
@ -766,6 +757,7 @@ bool select_box(const ViewContext &vc,
bool select_lasso(const ViewContext &vc,
bke::CurvesGeometry &curves,
const Span<float3> positions,
const float4x4 &projection_matrix,
const IndexMask &mask,
const bke::AttrDomain selection_domain,
const Span<int2> lasso_coords,
filedescriptor marked this conversation as resolved
Review

Commit this name change separately too maybe?

Commit this name change separately too maybe?
Review

Sure :)

Sure :)
@ -784,13 +776,11 @@ bool select_lasso(const ViewContext &vc,
changed = true;
}
const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obact);
const OffsetIndices points_by_curve = curves.points_by_curve();
if (selection_domain == bke::AttrDomain::Point) {
mask.foreach_index(GrainSize(1024), [&](const int point_i) {
const float2 pos_proj = ED_view3d_project_float_v2_m4(
vc.region, positions[point_i], projection);
vc.region, positions[point_i], projection_matrix);
/* Check the lasso bounding box first as an optimization. */
if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
BLI_lasso_is_point_inside(
@ -806,7 +796,7 @@ bool select_lasso(const ViewContext &vc,
const IndexRange points = points_by_curve[curve_i];
if (points.size() == 1) {
const float2 pos_proj = ED_view3d_project_float_v2_m4(
vc.region, positions[points.first()], projection);
vc.region, positions[points.first()], projection_matrix);
/* Check the lasso bounding box first as an optimization. */
if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
BLI_lasso_is_point_inside(
@ -821,8 +811,8 @@ bool select_lasso(const ViewContext &vc,
const float3 pos1 = positions[segment_i];
const float3 pos2 = positions[segment_i + 1];
const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection_matrix);
const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection_matrix);
/* Check the lasso bounding box first as an optimization. */
if (BLI_rcti_isect_segment(&bbox, int2(pos1_proj), int2(pos2_proj)) &&
@ -849,6 +839,7 @@ bool select_lasso(const ViewContext &vc,
bool select_circle(const ViewContext &vc,
bke::CurvesGeometry &curves,
const Span<float3> positions,
const float4x4 &projection,
const IndexMask &mask,
const bke::AttrDomain selection_domain,
const int2 coord,
@ -865,8 +856,6 @@ bool select_circle(const ViewContext &vc,
changed = true;
}
const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obact);
const OffsetIndices points_by_curve = curves.points_by_curve();
if (selection_domain == bke::AttrDomain::Point) {
mask.foreach_index(GrainSize(1024), [&](const int point_i) {

View File

@ -34,9 +34,12 @@ namespace blender::ed::greasepencil {
DrawingPlacement::DrawingPlacement(const Scene &scene,
const ARegion &region,
const View3D &view3d,
const Object &object)
: region_(&region), view3d_(&view3d), transforms_(object)
const Object &eval_object,
const bke::greasepencil::Layer &layer)
: region_(&region), view3d_(&view3d)
{
layer_space_to_world_space_ = layer.to_world_space(eval_object);
world_space_to_layer_space_ = math::invert(layer_space_to_world_space_);
/* Initialize DrawingPlacementPlane from toolsettings. */
switch (scene.toolsettings->gp_sculpt.lock_axis) {
case GP_LOCKAXIS_VIEW:
@ -66,7 +69,7 @@ DrawingPlacement::DrawingPlacement(const Scene &scene,
switch (scene.toolsettings->gpencil_v3d_align) {
case GP_PROJECT_VIEWSPACE:
depth_ = DrawingPlacementDepth::ObjectOrigin;
placement_loc_ = transforms_.layer_space_to_world_space.location();
placement_loc_ = layer_space_to_world_space_.location();
break;
case (GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR):
depth_ = DrawingPlacementDepth::Cursor;
@ -76,12 +79,12 @@ DrawingPlacement::DrawingPlacement(const Scene &scene,
depth_ = DrawingPlacementDepth::Surface;
surface_offset_ = scene.toolsettings->gpencil_surface_offset;
/* Default to view placement with the object origin if we don't hit a surface. */
placement_loc_ = transforms_.layer_space_to_world_space.location();
placement_loc_ = layer_space_to_world_space_.location();
break;
case (GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_STROKE):
depth_ = DrawingPlacementDepth::NearestStroke;
/* Default to view placement with the object origin if we don't hit a stroke. */
placement_loc_ = transforms_.layer_space_to_world_space.location();
placement_loc_ = layer_space_to_world_space_.location();
break;
}
@ -133,7 +136,7 @@ void DrawingPlacement::set_origin_to_nearest_stroke(const float2 co)
}
else {
/* If nothing was hit, use origin. */
placement_loc_ = transforms_.layer_space_to_world_space.location();
placement_loc_ = layer_space_to_world_space_.location();
}
plane_from_point_normal_v3(placement_plane_, placement_loc_, placement_normal_);
}
@ -169,7 +172,7 @@ float3 DrawingPlacement::project(const float2 co) const
ED_view3d_win_to_3d(view3d_, region_, placement_loc_, co, proj_point);
}
}
return math::transform_point(transforms_.world_space_to_layer_space, proj_point);
return math::transform_point(world_space_to_layer_space_, proj_point);
}
void DrawingPlacement::project(const Span<float2> src, MutableSpan<float3> dst) const

View File

@ -239,9 +239,9 @@ struct FindClosestData {
* \return A new point or curve closer than the \a initial input, if one exists.
*/
std::optional<FindClosestData> closest_elem_find_screen_space(const ViewContext &vc,
const Object &object,
OffsetIndices<int> points_by_curve,
Span<float3> deformed_positions,
const float4x4 &projection,
const IndexMask &mask,
bke::AttrDomain domain,
int2 coord,
@ -253,6 +253,7 @@ std::optional<FindClosestData> closest_elem_find_screen_space(const ViewContext
bool select_box(const ViewContext &vc,
bke::CurvesGeometry &curves,
Span<float3> deformed_positions,
const float4x4 &projection,
const IndexMask &mask,
bke::AttrDomain selection_domain,
const rcti &rect,
@ -264,9 +265,10 @@ bool select_box(const ViewContext &vc,
bool select_lasso(const ViewContext &vc,
bke::CurvesGeometry &curves,
Span<float3> deformed_positions,
const float4x4 &projection_matrix,
filedescriptor marked this conversation as resolved Outdated

const reference (applies elsewhere in the PR too, I won't write it everywhere though)

const reference (applies elsewhere in the PR too, I won't write it everywhere though)
const IndexMask &mask,
bke::AttrDomain selection_domain,
Span<int2> coords,
Span<int2> lasso_coords,
eSelectOp sel_op);
/**
@ -275,6 +277,7 @@ bool select_lasso(const ViewContext &vc,
bool select_circle(const ViewContext &vc,
bke::CurvesGeometry &curves,
Span<float3> deformed_positions,
const float4x4 &projection,
const IndexMask &mask,
bke::AttrDomain selection_domain,
int2 coord,

View File

@ -70,7 +70,6 @@ class DrawingPlacement {
DrawingPlacementDepth depth_;
DrawingPlacementPlane plane_;
bke::greasepencil::DrawingTransforms transforms_;
ViewDepths *depth_cache_ = nullptr;
float surface_offset_;
@ -78,12 +77,16 @@ class DrawingPlacement {
float3 placement_normal_;
float4 placement_plane_;
float4x4 layer_space_to_world_space_;
float4x4 world_space_to_layer_space_;
public:
DrawingPlacement() = default;
DrawingPlacement(const Scene &scene,
const ARegion &region,
const View3D &view3d,
const Object &object);
const Object &eval_object,
const bke::greasepencil::Layer &layer);
~DrawingPlacement();
public:

View File

@ -61,13 +61,7 @@ struct EraseOperationExecutor {
int2 mouse_position_pixels{};
int64_t eraser_squared_radius_pixels{};
bke::greasepencil::DrawingTransforms transforms_;
EraseOperationExecutor(const bContext &C)
{
Object *object = CTX_data_active_object(&C);
transforms_ = bke::greasepencil::DrawingTransforms(*object);
}
EraseOperationExecutor(const bContext & /*C*/) {}
/**
* Computes the intersections between a 2D line segment and a circle with integer values.
@ -759,50 +753,51 @@ struct EraseOperationExecutor {
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(obact->data);
bool changed = false;
const auto execute_eraser_on_drawing =
[&](const int layer_index, const int frame_number, Drawing &drawing) {
const bke::CurvesGeometry &src = drawing.strokes();
const auto execute_eraser_on_drawing = [&](const int layer_index,
const int frame_number,
Drawing &drawing) {
const Layer &layer = *grease_pencil.layers()[layer_index];
const bke::CurvesGeometry &src = drawing.strokes();
/* Evaluated geometry. */
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *obact, layer_index, frame_number);
/* Evaluated geometry. */
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *obact, layer_index, frame_number);
/* Compute screen space positions. */
Array<float2> screen_space_positions(src.points_num());
threading::parallel_for(src.points_range(), 4096, [&](const IndexRange src_points) {
for (const int src_point : src_points) {
ED_view3d_project_float_global(
region,
math::transform_point(transforms_.layer_space_to_world_space,
deformation.positions[src_point]),
screen_space_positions[src_point],
V3D_PROJ_TEST_NOP);
}
});
/* Compute screen space positions. */
Array<float2> screen_space_positions(src.points_num());
threading::parallel_for(src.points_range(), 4096, [&](const IndexRange src_points) {
for (const int src_point : src_points) {
ED_view3d_project_float_global(region,
math::transform_point(layer.to_world_space(*ob_eval),
deformation.positions[src_point]),
screen_space_positions[src_point],
V3D_PROJ_TEST_NOP);
}
});
/* Erasing operator. */
bke::CurvesGeometry dst;
bool erased = false;
switch (self.eraser_mode) {
case GP_BRUSH_ERASER_STROKE:
erased = stroke_eraser(src, screen_space_positions, dst);
break;
case GP_BRUSH_ERASER_HARD:
erased = hard_eraser(src, screen_space_positions, dst, self.keep_caps);
break;
case GP_BRUSH_ERASER_SOFT:
// To be implemented
return;
}
/* Erasing operator. */
bke::CurvesGeometry dst;
bool erased = false;
switch (self.eraser_mode) {
case GP_BRUSH_ERASER_STROKE:
erased = stroke_eraser(src, screen_space_positions, dst);
break;
case GP_BRUSH_ERASER_HARD:
erased = hard_eraser(src, screen_space_positions, dst, self.keep_caps);
break;
case GP_BRUSH_ERASER_SOFT:
// To be implemented
return;
}
if (erased) {
/* Set the new geometry. */
drawing.geometry.wrap() = std::move(dst);
drawing.tag_topology_changed();
changed = true;
}
};
if (erased) {
/* Set the new geometry. */
drawing.geometry.wrap() = std::move(dst);
drawing.tag_topology_changed();
changed = true;
}
};
if (self.active_layer_only) {
/* Erase only on the drawing at the current frame of the active layer. */

View File

@ -424,10 +424,12 @@ struct PaintOperationExecutor {
void PaintOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
ARegion *region = CTX_wm_region(&C);
View3D *view3d = CTX_wm_view3d(&C);
Scene *scene = CTX_data_scene(&C);
Object *object = CTX_data_active_object(&C);
Object *eval_object = DEG_get_evaluated_object(depsgraph, object);
GreasePencil *grease_pencil = static_cast<GreasePencil *>(object->data);
Paint *paint = &scene->toolsettings->gp_paint->paint;
@ -444,7 +446,8 @@ void PaintOperation::on_stroke_begin(const bContext &C, const InputSample &start
BKE_curvemapping_init(brush->gpencil_settings->curve_rand_value);
/* Initialize helper class for projecting screen space coordinates. */
placement_ = ed::greasepencil::DrawingPlacement(*scene, *region, *view3d, *object);
placement_ = ed::greasepencil::DrawingPlacement(
*scene, *region, *view3d, *eval_object, *grease_pencil->get_active_layer());
if (placement_.use_project_to_surface()) {
placement_.cache_viewport_depths(CTX_data_depsgraph_pointer(&C), region, view3d);
}

View File

@ -1197,6 +1197,7 @@ static bool do_lasso_select_grease_pencil(ViewContext *vc,
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*vc->scene, grease_pencil);
for (const ed::greasepencil::MutableDrawingInfo info : drawings) {
const bke::greasepencil::Layer &layer = *grease_pencil.layers()[info.layer_index];
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, info.layer_index, info.frame_number);
@ -1207,10 +1208,13 @@ static bool do_lasso_select_grease_pencil(ViewContext *vc,
if (elements.is_empty()) {
continue;
}
const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(vc->rv3d, layer_to_world);
changed = ed::curves::select_lasso(
*vc,
info.drawing.strokes_for_write(),
deformation.positions,
projection,
elements,
selection_domain,
Span<int2>(reinterpret_cast<const int2 *>(mcoords), mcoords_len),
@ -1432,10 +1436,12 @@ static bool view3d_lasso_select(bContext *C,
bke::crazyspace::get_evaluated_curves_deformation(*vc->depsgraph, *vc->obedit);
const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
const IndexRange elements(curves.attributes().domain_size(selection_domain));
const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
changed = ed::curves::select_lasso(
*vc,
curves,
deformation.positions,
projection,
elements,
selection_domain,
Span<int2>(reinterpret_cast<const int2 *>(mcoords), mcoords_len),
@ -3109,12 +3115,13 @@ static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPi
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*vc.depsgraph, *vc.obedit);
const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, &curves_ob);
const IndexRange elements(curves.attributes().domain_size(selection_domain));
std::optional<ed::curves::FindClosestData> new_closest_elem =
ed::curves::closest_elem_find_screen_space(vc,
curves_ob,
curves.points_by_curve(),
deformation.positions,
projection,
elements,
selection_domain,
mval,
@ -3208,6 +3215,7 @@ static bool ed_grease_pencil_select_pick(bContext *C,
ClosestGreasePencilDrawing new_closest = init;
for (const int i : range) {
ed::greasepencil::MutableDrawingInfo info = drawings[i];
const bke::greasepencil::Layer &layer = *grease_pencil.layers()[info.layer_index];
/* Get deformation by modifiers. */
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
@ -3219,11 +3227,14 @@ static bool ed_grease_pencil_select_pick(bContext *C,
if (elements.is_empty()) {
continue;
}
const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(vc.rv3d,
layer_to_world);
std::optional<ed::curves::FindClosestData> new_closest_elem =
ed::curves::closest_elem_find_screen_space(vc,
*vc.obedit,
info.drawing.strokes().points_by_curve(),
deformation.positions,
projection,
elements,
selection_domain,
mval,
@ -4216,6 +4227,7 @@ static bool do_grease_pencil_box_select(ViewContext *vc, const rcti *rect, const
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
for (const ed::greasepencil::MutableDrawingInfo info : drawings) {
const bke::greasepencil::Layer &layer = *grease_pencil.layers()[info.layer_index];
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, info.layer_index, info.frame_number);
@ -4225,9 +4237,12 @@ static bool do_grease_pencil_box_select(ViewContext *vc, const rcti *rect, const
if (elements.is_empty()) {
continue;
}
const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(vc->rv3d, layer_to_world);
changed |= ed::curves::select_box(*vc,
info.drawing.strokes_for_write(),
deformation.positions,
projection,
elements,
selection_domain,
*rect,
@ -4313,9 +4328,16 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*vc.depsgraph, *vc.obedit);
const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obedit);
const IndexRange elements(curves.attributes().domain_size(selection_domain));
changed = ed::curves::select_box(
vc, curves, deformation.positions, elements, selection_domain, rect, sel_op);
changed = ed::curves::select_box(vc,
curves,
deformation.positions,
projection,
elements,
selection_domain,
rect,
sel_op);
if (changed) {
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
* generic attribute for now. */
@ -5070,6 +5092,7 @@ static bool grease_pencil_circle_select(ViewContext *vc,
const Array<ed::greasepencil::MutableDrawingInfo> drawings =
ed::greasepencil::retrieve_editable_drawings(*vc->scene, grease_pencil);
for (const ed::greasepencil::MutableDrawingInfo info : drawings) {
const bke::greasepencil::Layer &layer = *grease_pencil.layers()[info.layer_index];
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
ob_eval, *vc->obedit, info.layer_index, info.frame_number);
@ -5079,9 +5102,12 @@ static bool grease_pencil_circle_select(ViewContext *vc,
if (elements.is_empty()) {
continue;
}
const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
const float4x4 projection = ED_view3d_ob_project_mat_get_from_obmat(vc->rv3d, layer_to_world);
changed = ed::curves::select_circle(*vc,
info.drawing.strokes_for_write(),
deformation.positions,
projection,
elements,
selection_domain,
int2(mval),
@ -5138,9 +5164,17 @@ static bool obedit_circle_select(bContext *C,
bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(*vc->depsgraph, *vc->obedit);
const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
const IndexRange elements(curves.attributes().domain_size(selection_domain));
changed = ed::curves::select_circle(
*vc, curves, deformation.positions, elements, selection_domain, mval, rad, sel_op);
changed = ed::curves::select_circle(*vc,
curves,
deformation.positions,
projection,
elements,
selection_domain,
mval,
rad,
sel_op);
if (changed) {
/* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
* generic attribute for now. */

View File

@ -117,6 +117,7 @@ void animrecord_check_state(TransInfo *t, ID *id);
*/
void curve_populate_trans_data_structs(TransDataContainer &tc,
blender::bke::CurvesGeometry &curves,
const blender::float4x4 &matrix,
std::optional<blender::MutableSpan<float>> value_attribute,
const blender::IndexMask &selected_indices,
bool use_proportional_edit,

View File

@ -95,7 +95,8 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t)
if (tc.data_len == 0) {
continue;
}
Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
Object *object = tc.obedit;
Curves *curves_id = static_cast<Curves *>(object->data);
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
std::optional<MutableSpan<float>> value_attribute;
@ -117,6 +118,7 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t)
curve_populate_trans_data_structs(tc,
curves,
float4x4(object->object_to_world),
value_attribute,
selection_per_object[i],
use_proportional_edit,
@ -153,9 +155,10 @@ static void recalcData_curves(TransInfo *t)
void curve_populate_trans_data_structs(TransDataContainer &tc,
blender::bke::CurvesGeometry &curves,
const blender::float4x4 &transform,
filedescriptor marked this conversation as resolved Outdated

matrix -> transform

`matrix` -> `transform`
std::optional<blender::MutableSpan<float>> value_attribute,
const blender::IndexMask &selected_indices,
bool use_proportional_edit,
const bool use_proportional_edit,
const blender::IndexMask &affected_curves,
bool use_connected_only,
int trans_data_offset)
@ -163,7 +166,7 @@ void curve_populate_trans_data_structs(TransDataContainer &tc,
using namespace blender;
float mtx[3][3], smtx[3][3];
copy_m3_m4(mtx, tc.obedit->object_to_world);
copy_m3_m4(mtx, transform.ptr());
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
MutableSpan<float3> positions = curves.positions_for_write();

View File

@ -10,6 +10,8 @@
#include "BKE_context.hh"
#include "DEG_depsgraph_query.hh"
#include "ED_curves.hh"
#include "ED_grease_pencil.hh"
@ -24,6 +26,7 @@ namespace blender::ed::transform::greasepencil {
static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Scene *scene = CTX_data_scene(C);
Object *object = CTX_data_active_object(C);
MutableSpan<TransDataContainer> trans_data_contrainers(t->data_container, t->data_container_len);
@ -81,15 +84,15 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
if (tc.data_len == 0) {
continue;
}
float mtx[3][3], smtx[3][3];
copy_m3_m4(mtx, tc.obedit->object_to_world);
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
Object *object_eval = DEG_get_evaluated_object(depsgraph, tc.obedit);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object_eval->data);
Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
int layer_points_offset = 0;
const Array<ed::greasepencil::MutableDrawingInfo> drawings = all_drawings[i];
for (ed::greasepencil::MutableDrawingInfo info : drawings) {
const bke::greasepencil::Layer &layer = *layers[info.layer_index];
const float4x4 layer_space_to_world_space = layer.to_world_space(*object_eval);
bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
const IndexMask points = points_per_layer_per_object[layer_offset];
@ -108,6 +111,7 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
*object, info.drawing, memory);
curve_populate_trans_data_structs(tc,
curves,
layer_space_to_world_space,
value_attribute,
points,
true,
@ -118,6 +122,7 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
else {
curve_populate_trans_data_structs(tc,
curves,
layer_space_to_world_space,
value_attribute,
points,
false,

View File

@ -292,6 +292,17 @@ typedef struct GreasePencilLayer {
* List of `GreasePencilLayerMask`.
*/
ListBase masks;
/**
* Layer parent object. Can be an armature in which case the `parsubstr` is the bone name.
*/
struct Object *parent;
char *parsubstr;
filedescriptor marked this conversation as resolved Outdated

How about using char * to take up less space when it isn't used? (and to simplify assignment, etc.)

How about using `char *` to take up less space when it isn't used? (and to simplify assignment, etc.)
/**
* Layer transform UI settings. These should *not* be used to do any computation.
* Use the functions is the `bke::greasepencil::Layer` class instead.
*/
float translation[3], rotation[3], scale[3];
char _pad2[4];
/**
* Runtime struct pointer.
*/

View File

@ -27,6 +27,7 @@
# include "BLI_span.hh"
# include "DEG_depsgraph.hh"
# include "DEG_depsgraph_build.hh"
static GreasePencil *rna_grease_pencil(const PointerRNA *ptr)
{
@ -36,7 +37,14 @@ static GreasePencil *rna_grease_pencil(const PointerRNA *ptr)
static void rna_grease_pencil_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr)
{
DEG_id_tag_update(&rna_grease_pencil(ptr)->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, nullptr);
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, rna_grease_pencil(ptr));
}
static void rna_grease_pencil_dependency_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)
filedescriptor marked this conversation as resolved

This should be ID_RECALC_GEOMETRY, since here the transform is getting applied to the geometry. ID_RECALC_TRANSFORM is for when the object matrix is affected.

This should be `ID_RECALC_GEOMETRY`, since here the transform is getting applied to the geometry. `ID_RECALC_TRANSFORM` is for when the object matrix is affected.
{
DEG_id_tag_update(&rna_grease_pencil(ptr)->id, ID_RECALC_GEOMETRY);
filedescriptor marked this conversation as resolved Outdated

Same here, this doesn't seem like the appropriate modifier if the mechanism is different. Probably NC_GPENCIL | NA_EDITED is better.

Same here, this doesn't seem like the appropriate modifier if the mechanism is different. Probably `NC_GPENCIL | NA_EDITED` is better.
DEG_relations_tag_update(bmain);
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, rna_grease_pencil(ptr));
}
static void rna_iterator_grease_pencil_layers_begin(CollectionPropertyIterator *iter,
@ -181,6 +189,8 @@ static void rna_def_grease_pencil_layer(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
const float scale_defaults[3] = {1.0f, 1.0f, 1.0f};
srna = RNA_def_struct(brna, "GreasePencilLayer", nullptr);
RNA_def_struct_sdna(srna, "GreasePencilLayer");
RNA_def_struct_ui_text(srna, "Grease Pencil Layer", "Collection of related drawings");
@ -226,6 +236,41 @@ static void rna_def_grease_pencil_layer(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Onion Skinning", "Display onion skins before and after the current frame");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Object");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Parent", "Parent object");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_dependency_update");
prop = RNA_def_property(srna, "parent_bone", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "parsubstr");
RNA_def_property_ui_text(
prop, "Parent Bone", "Name of parent bone. Only used when the parent object is an armature");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_dependency_update");
prop = RNA_def_property(srna, "translation", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 3);
RNA_def_property_float_sdna(prop, nullptr, "translation");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_ui_text(prop, "Translation", "Translation of the layer");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_EULER);
RNA_def_property_array(prop, 3);
RNA_def_property_float_sdna(prop, nullptr, "rotation");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_ui_text(prop, "Rotation", "Euler rotation of the layer");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ);
RNA_def_property_array(prop, 3);
RNA_def_property_float_sdna(prop, nullptr, "scale");
RNA_def_property_float_array_default(prop, scale_defaults);
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3);
RNA_def_property_ui_text(prop, "Scale", "Scale of the layer");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_grease_pencil_update");
}
static void rna_def_grease_pencil_layers_api(BlenderRNA *brna, PropertyRNA *cprop)