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 218 additions and 4 deletions
Showing only changes of commit 493b061a96 - Show all commits

View File

@ -413,9 +413,23 @@ void legacy_gpencil_to_grease_pencil(GreasePencil &grease_pencil, bGPdata &gpd);
using namespace blender::bke::gpencil;
struct StrokePoint {
float3 position;
float radius;
float opacity;
float4 color;
};
class GreasePencilDrawingRuntime {
public:
mutable SharedCache<Vector<uint3>> triangles_cache;
/**
* Stroke cache for a stroke that is currently being drawn.
*/
Vector<StrokePoint> stroke_cache = {};
Vector<uint3> stroke_triangle_cache = {};
int stroke_mat = 0;
};
class GreasePencilRuntime {

View File

@ -428,6 +428,16 @@ void GreasePencilDrawing::tag_positions_changed()
this->runtime->triangles_cache.tag_dirty();
}
bool GreasePencilDrawing::has_stroke_buffer()
{
return this->runtime->stroke_cache.size() > 0;
}

.as_span() shouldn't be necessary here.

`.as_span()` shouldn't be necessary here.
blender::Span<blender::bke::StrokePoint> GreasePencilDrawing::stroke_buffer()
{

int frame -> const int frame

`int frame` -> `const int frame`
return this->runtime->stroke_cache.as_span();
}
/* GreasePencil API */
blender::Span<GreasePencilDrawingOrReference *> GreasePencil::drawings() const
@ -462,6 +472,19 @@ void GreasePencil::foreach_visible_drawing(
}
}
blender::bke::gpencil::Layer *GreasePencil::get_active_layer()
{
using namespace blender::bke::gpencil;
/* TOOD. For now get the first layer. */
for (TreeNode &node : this->runtime->root_group().children_in_pre_order()) {
if (!node.is_layer()) {
continue;
}
return &node.as_layer();
}
return nullptr;

I believe it's standard to std::move from a T &&

Is there a need for overloads for T & and T &&? What's the benefit of that?

I believe it's standard to `std::move` from a `T &&` Is there a need for overloads for `T &` and `T &&`? What's the benefit of that?
}
void GreasePencil::read_drawing_array(BlendDataReader *reader)
{
BLO_read_pointer_array(reader, (void **)&this->drawing_array);

View File

@ -1945,6 +1945,7 @@ bool BKE_object_data_is_in_editmode(const Object *ob, const ID *id)
case ID_AR:
return ((const bArmature *)id)->edbo != nullptr;
case ID_CV:
case ID_GP:
if (ob) {
return BKE_object_is_in_editmode(ob);
}

View File

@ -616,6 +616,8 @@ NodeType geometry_tag_to_component(const ID *id)
return NodeType::PARAMETERS;
case ID_MSK:
return NodeType::PARAMETERS;
case ID_GP:
return NodeType::GEOMETRY;
default:
break;
}

View File

@ -235,6 +235,15 @@ static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
total_triangles_num += (curves.points_num() + num_cyclic) * 2;
total_triangles_num += drawing.triangles().size();
if (drawing.has_stroke_buffer()) {
const int num_buffer_points = drawing.stroke_buffer().size();
total_points_num += 1 + num_buffer_points + 1;
total_triangles_num += num_buffer_points * 2;
verts_start_offsets.append(v);
/* TODO: triangles. */
v += 1 + num_buffer_points + 1;
}
verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets));
tris_start_offsets_per_visible_drawing.append(std::move(tris_start_offsets));
});
@ -352,6 +361,51 @@ static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
}
});
if (drawing.has_stroke_buffer()) {
Span<bke::StrokePoint> points = drawing.stroke_buffer();
const int verts_start_offset = verts_start_offsets.last();
const int num_verts = 1 + points.size() + 1;
IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
const int material_nr = drawing.runtime->stroke_mat;
verts_slice.first().mat = -1;
for (const int i : IndexRange(points.size())) {
const int idx = i + 1;
GreasePencilStrokeVert &s_vert = verts_slice[idx];
GreasePencilColorVert &c_vert = cols_slice[idx];
const bke::StrokePoint &point = points[i];
copy_v3_v3(s_vert.pos, point.position);
s_vert.radius = point.radius;
s_vert.opacity = point.opacity;
s_vert.point_id = verts_range[idx];
s_vert.stroke_id = verts_range.first();
s_vert.mat = material_nr;
/* TODO */
s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness(0.0f, 1.0f, 1.0f);
/* TODO */
s_vert.u_stroke = 0;
/* TODO */
s_vert.uv_fill[0] = s_vert.uv_fill[1] = 0;
/* TODO */
copy_v4_v4(c_vert.vcol, float4(0.0f, 0.0f, 0.0f, 0.0f));
copy_v4_v4(c_vert.fcol, float4(0.0f, 0.0f, 0.0f, 0.0f));
/* TODO */
c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + 1.0f;
int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT;
GPU_indexbuf_add_tri_verts(&ibo, v_mat + 0, v_mat + 1, v_mat + 2);
GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3);
}
verts_slice.last().mat = -1;
}
drawing_i++;
});

View File

@ -1,6 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2023 Blender Foundation. */
#include "BKE_context.h"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.h"
#include "BKE_grease_pencil.hh"
#include "BKE_scene.h"
#include "DEG_depsgraph_query.h"
#include "ED_view3d.h"
#include "WM_api.h"
#include "WM_types.h"
#include "grease_pencil_intern.hh"
namespace blender::ed::sculpt_paint::gpencil {
@ -24,9 +37,31 @@ struct PaintOperationExecutor {
void execute(PaintOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
{
std::cout << "pos: (" << stroke_extension.mouse_position.x << ", "
<< stroke_extension.mouse_position.x << "), pressure: " << stroke_extension.pressure
<< std::endl;
using namespace blender::bke;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
Scene *scene = CTX_data_scene(&C);
ARegion *region = CTX_wm_region(&C);
Object *obact = CTX_data_active_object(&C);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_eval->data);
Review

It seems a bit weird to modifier the evaluated object here. Is that purposeful? Or am I missing something?

It seems a bit weird to modifier the evaluated object here. Is that purposeful? Or am I missing something?
Review

Yes, this is done on purpose so that the extra copy from orig to eval for rendering is not needed every update. Once drawing is done, the stroke is copied from the eval buffer to orig.

Yes, this is done on purpose so that the extra copy from orig to eval for rendering is not needed every update. Once drawing is done, the stroke is copied from the eval buffer to orig.
Review

Okay, makes sense. This definitely deserves a comment, since typically changing the evaluated data isn't a good idea.

Okay, makes sense. This definitely deserves a comment, since typically changing the evaluated data isn't a good idea.
bke::gpencil::Layer *active_layer = grease_pencil.get_active_layer();
BLI_assert(active_layer != nullptr);
int index = active_layer->drawing_at(scene->r.cfra);
BLI_assert(index != -1);
GreasePencilDrawing &drawing = *reinterpret_cast<GreasePencilDrawing *>(
grease_pencil.drawings()[index]);
float4 plane{0.0f, -1.0f, 0.0f, 0.0f};
float3 proj_pos;
ED_view3d_win_to_3d_on_plane(region, plane, stroke_extension.mouse_position, false, proj_pos);
StrokePoint new_point{proj_pos, stroke_extension.pressure * 100.0f, 1.0f, float4(1.0f)};
drawing.runtime->stroke_cache.append(std::move(new_point));
BKE_grease_pencil_batch_cache_dirty_tag(&grease_pencil, BKE_GREASEPENCIL_BATCH_DIRTY_ALL);
}
};
@ -38,7 +73,88 @@ void PaintOperation::on_stroke_extended(const bContext &C, const StrokeExtension
void PaintOperation::on_stroke_done(const bContext &C)
{
std::cout << "Done!" << std::endl;
using namespace blender::bke;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
Scene *scene = CTX_data_scene(&C);
Object *obact = CTX_data_active_object(&C);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact);
GreasePencil &grease_pencil_orig = *static_cast<GreasePencil *>(obact->data);
GreasePencil &grease_pencil_eval = *static_cast<GreasePencil *>(ob_eval->data);
bke::gpencil::Layer *active_layer_orig = grease_pencil_orig.get_active_layer();
bke::gpencil::Layer *active_layer_eval = grease_pencil_eval.get_active_layer();
BLI_assert(active_layer_orig != nullptr && active_layer_eval != nullptr);
int index_orig = active_layer_orig->drawing_at(scene->r.cfra);
int index_eval = active_layer_eval->drawing_at(scene->r.cfra);
BLI_assert(index_orig != -1 && index_eval != -1);
GreasePencilDrawing &drawing_orig = *reinterpret_cast<GreasePencilDrawing *>(
grease_pencil_orig.drawings()[index_orig]);
GreasePencilDrawing &drawing_eval = *reinterpret_cast<GreasePencilDrawing *>(
grease_pencil_eval.drawings()[index_eval]);
const Span<StrokePoint> stroke_points = drawing_eval.stroke_buffer();
std::cout << stroke_points.size() << std::endl;
CurvesGeometry &curves = drawing_orig.geometry.wrap();
int num_old_curves = curves.curves_num();
int num_old_points = curves.points_num();
curves.resize(num_old_points + stroke_points.size(), num_old_curves + 1);
curves.offsets_for_write()[num_old_curves] = num_old_points;
curves.offsets_for_write()[num_old_curves + 1] = num_old_points + stroke_points.size();
const offset_indices::OffsetIndices<int> points_by_curve = curves.points_by_curve();
const IndexRange new_points_range = points_by_curve[curves.curves_num() - 1];
const IndexRange new_curves_range = IndexRange(num_old_curves, 1);
/* Set position, radius and opacity attribute. */
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
filedescriptor marked this conversation as resolved Outdated

offset_indices::OffsetIndices -> OffsetIndices

`offset_indices::OffsetIndices` -> `OffsetIndices`
MutableSpan<float3> positions = curves.positions_for_write();
SpanAttributeWriter<float> radii = attributes.lookup_for_write_span<float>("radius");
SpanAttributeWriter<float> opacities = attributes.lookup_for_write_span<float>("opacity");
for (const int i : IndexRange(stroke_points.size())) {
const StrokePoint &point = stroke_points[i];
const int point_i = new_points_range[i];
positions[point_i] = point.position;
radii.span[point_i] = point.radius;
opacities.span[point_i] = point.opacity;
}
/* Set material index attribute. */
int material_index = 0;
SpanAttributeWriter<int> materials = attributes.lookup_for_write_span<int>("material_index");
materials.span.slice(new_curves_range).fill(material_index);
/* Set curve_type attribute. */
curves.fill_curve_types(new_curves_range, CURVE_TYPE_POLY);
/* Explicitly set all other attributes besides those processed above to default values. */
Set<std::string> attributes_to_skip{
{"position", "radius", "opacity", "material_index", "curve_type"}};
attributes.for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
if (attributes_to_skip.contains(id.name())) {
return true;
}
bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
const CPPType &type = attribute.span.type();
GMutableSpan new_data = attribute.span.slice(
attribute.domain == ATTR_DOMAIN_POINT ? new_points_range : new_curves_range);
type.fill_assign_n(type.default_value(), new_data.data(), new_data.size());
attribute.finish();
return true;
});
drawing_eval.runtime->stroke_cache.clear_and_shrink();
drawing_orig.tag_positions_changed();
radii.finish();
opacities.finish();
materials.finish();
DEG_id_tag_update(&grease_pencil_orig.id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, &grease_pencil_orig.id);
}
std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation()

View File

@ -17,6 +17,7 @@
namespace blender::bke {
class GreasePencilRuntime;
class GreasePencilDrawingRuntime;
struct StrokePoint;
namespace gpencil {
class Layer;
}
@ -70,6 +71,8 @@ typedef struct GreasePencilDrawing {
*/
blender::Span<blender::uint3> triangles() const;
void tag_positions_changed();
bool has_stroke_buffer();
blender::Span<blender::bke::StrokePoint> stroke_buffer();
#endif
/**
* Runtime data on the drawing.
@ -184,6 +187,7 @@ typedef struct GreasePencil {
/* Only used for storage in the .blend file. */
GreasePencilLayerTreeStorage layer_tree_storage;
#ifdef __cplusplus
blender::bke::gpencil::Layer *get_active_layer();
void save_layer_tree_to_storage();
void load_layer_tree_from_storage();
void read_layer_tree_storage(BlendDataReader *reader);