Initial Grease Pencil 3.0 stage #106848
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
blender::Span<blender::bke::StrokePoint> GreasePencilDrawing::stroke_buffer()
|
||||
{
|
||||
Hans Goudey
commented
`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;
|
||||
Hans Goudey
commented
I believe it's standard to Is there a need for overloads for 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++;
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
Hans Goudey
commented
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?
Falk David
commented
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.
Hans Goudey
commented
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
Hans Goudey
commented
`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()
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
.as_span()
shouldn't be necessary here.