Initial Grease Pencil 3.0 stage #106848
|
@ -442,6 +442,9 @@ class GreasePencilRuntime {
|
|||
{
|
||||
return root_group_;
|
||||
}
|
||||
|
||||
public:
|
||||
void *batch_cache = nullptr;
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
|
|
|
@ -73,6 +73,7 @@ set(SRC
|
|||
intern/draw_cache_impl_curve.cc
|
||||
intern/draw_cache_impl_curves.cc
|
||||
intern/draw_cache_impl_gpencil_legacy.cc
|
||||
intern/draw_cache_impl_grease_pencil.cc
|
||||
intern/draw_cache_impl_lattice.c
|
||||
intern/draw_cache_impl_mesh.cc
|
||||
intern/draw_cache_impl_particles.c
|
||||
|
|
|
@ -138,11 +138,9 @@ class Instance {
|
|||
void object_sync(Manager &manager, ObjectRef &object_ref)
|
||||
{
|
||||
switch (object_ref.object->type) {
|
||||
// case OB_GPENCIL_LEGACY:
|
||||
// objects.sync_gpencil(manager, object_ref, main_fb_, main_ps_);
|
||||
// break;
|
||||
case OB_GREASE_PENCIL:
|
||||
// objects.sync_gpencil(manager, object_ref, main_fb_, main_ps_);
|
||||
objects.sync_grease_pencil(manager, object_ref, main_fb_, main_ps_);
|
||||
break;
|
||||
case OB_LAMP:
|
||||
lights.sync(object_ref);
|
||||
break;
|
||||
|
|
|
@ -112,10 +112,10 @@ class ObjectModule {
|
|||
// objects_buf_.shrink();
|
||||
}
|
||||
|
||||
void sync_gpencil(Manager &manager,
|
||||
ObjectRef &object_ref,
|
||||
Framebuffer &main_fb,
|
||||
PassSortable &main_ps)
|
||||
void sync_grease_pencil(Manager &manager,
|
||||
ObjectRef &object_ref,
|
||||
Framebuffer &main_fb,
|
||||
PassSortable &main_ps)
|
||||
{
|
||||
using namespace blender::bke::gpencil;
|
||||
|
||||
|
@ -126,7 +126,7 @@ class ObjectModule {
|
|||
return;
|
||||
}
|
||||
|
||||
const bool is_stroke_order_3d = true; /* TODO */
|
||||
const bool is_stroke_order_3d = false; /* TODO */
|
||||
bool do_material_holdout = false;
|
||||
bool do_layer_blending = false;
|
||||
bool object_has_vfx = false; // TODO vfx.object_has_vfx(gpd);
|
||||
|
@ -171,9 +171,10 @@ class ObjectModule {
|
|||
ob.layer_offset = layer_offset;
|
||||
ob.material_offset = material_offset;
|
||||
|
||||
GPUVertBuf *position_tx = DRW_cache_gpencil_position_buffer_get(object, current_frame_);
|
||||
GPUVertBuf *color_tx = DRW_cache_gpencil_color_buffer_get(object, current_frame_);
|
||||
GPUBatch *geom = DRW_cache_gpencil_get(object, current_frame_);
|
||||
GPUVertBuf *position_tx = DRW_cache_grease_pencil_position_buffer_get(object,
|
||||
current_frame_);
|
||||
GPUVertBuf *color_tx = DRW_cache_grease_pencil_color_buffer_get(object, current_frame_);
|
||||
GPUBatch *geom = DRW_cache_grease_pencil_get(object, current_frame_);
|
||||
|
||||
if (do_layer_blending) {
|
||||
// for (const LayerData &layer : frame.layers) {
|
||||
|
|
|
@ -253,7 +253,7 @@ DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume,
|
|||
struct GPUBatch *DRW_cache_volume_face_wireframe_get(struct Object *ob);
|
||||
struct GPUBatch *DRW_cache_volume_selection_surface_get(struct Object *ob);
|
||||
|
||||
/* GPencil */
|
||||
/* GPencil (legacy) */
|
||||
|
||||
struct GPUBatch *DRW_cache_gpencil_get(struct Object *ob, int cfra);
|
||||
|
||||
struct GPUVertBuf *DRW_cache_gpencil_position_buffer_get(struct Object *ob, int cfra);
|
||||
|
@ -276,6 +276,12 @@ struct bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(struct Object *ob);
|
|||
*/
|
||||
void DRW_cache_gpencil_sbuffer_clear(struct Object *ob);
|
||||
|
||||
/* Grease Pencil */
|
||||
|
||||
struct GPUBatch *DRW_cache_grease_pencil_get(struct Object *ob, int cfra);
|
||||
struct GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(struct Object *ob, int cfra);
|
||||
struct GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(struct Object *ob, int cfra);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,329 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Grease Pencil API for render engines
|
||||
*/
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_render.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
|
||||
#include "../engines/gpencil/gpencil_defines.h"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
struct GreasePencilBatchCache {
|
||||
/** Instancing Data */
|
||||
GPUVertBuf *vbo;
|
||||
GPUVertBuf *vbo_col;
|
||||
/** Indices in material order, then stroke order with fill first.
|
||||
* Strokes can be individually rendered using `gps->runtime.stroke_start` and
|
||||
* `gps->runtime.fill_start`. */
|
||||
GPUIndexBuf *ibo;
|
||||
/** Batches */
|
||||
GPUBatch *geom_batch;
|
||||
filedescriptor marked this conversation as resolved
Outdated
Falk David
commented
Update this comment. Out of date. Update this comment. Out of date.
|
||||
/** Stroke lines only */
|
||||
GPUBatch *lines_batch;
|
||||
|
||||
/** Cache is dirty. */
|
||||
bool is_dirty;
|
||||
/** Last cached frame. */
|
||||
int cache_frame;
|
||||
filedescriptor marked this conversation as resolved
Falk David
commented
Remove while unused? Remove while unused?
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Vertex Formats
|
||||
* \{ */
|
||||
|
||||
/* MUST match the format below. */
|
||||
struct GreasePencilStrokeVert {
|
||||
/** Position and radius packed in the same attribute. */
|
||||
float pos[3], radius;
|
||||
/** Material Index, Stroke Index, Point Index, Packed aspect + hardness + rotation. */
|
||||
int32_t mat, stroke_id, point_id, packed_asp_hard_rot;
|
||||
/** UV and opacity packed in the same attribute. */
|
||||
float uv_fill[2], u_stroke, opacity;
|
||||
};
|
||||
|
||||
static GPUVertFormat *grease_pencil_stroke_format()
|
||||
{
|
||||
static GPUVertFormat format = {0};
|
||||
if (format.attr_len == 0) {
|
||||
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
GPU_vertformat_attr_add(&format, "ma", GPU_COMP_I32, 4, GPU_FETCH_INT);
|
||||
GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
}
|
||||
return &format;
|
||||
}
|
||||
|
||||
/* MUST match the format below. */
|
||||
struct GreasePencilColorVert {
|
||||
float vcol[4]; /* Vertex color */
|
||||
float fcol[4]; /* Fill color */
|
||||
};
|
||||
|
||||
static GPUVertFormat *grease_pencil_color_format()
|
||||
{
|
||||
static GPUVertFormat format = {0};
|
||||
if (format.attr_len == 0) {
|
||||
GPU_vertformat_attr_add(&format, "col", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
GPU_vertformat_attr_add(&format, "fcol", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
||||
}
|
||||
return &format;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Utilities
|
||||
* \{ */
|
||||
|
||||
static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
const GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
return (cache && cache->is_dirty == false);
|
||||
}
|
||||
|
||||
static GreasePencilBatchCache *grease_pencil_batch_cache_init(GreasePencil &grease_pencil,
|
||||
int cfra)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
if (!cache) {
|
||||
cache = MEM_new<GreasePencilBatchCache>(__func__);
|
||||
grease_pencil.runtime->batch_cache = cache;
|
||||
}
|
||||
else {
|
||||
grease_pencil.runtime->batch_cache = {};
|
||||
}
|
||||
|
||||
cache->is_dirty = false;
|
||||
cache->cache_frame = cfra;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
static void grese_pencil_batch_cache_clear(GreasePencil &grease_pencil)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
if (cache == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_BATCH_DISCARD_SAFE(cache->lines_batch);
|
||||
GPU_BATCH_DISCARD_SAFE(cache->geom_batch);
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->vbo);
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->vbo_col);
|
||||
GPU_INDEXBUF_DISCARD_SAFE(cache->ibo);
|
||||
|
||||
cache->is_dirty = true;
|
||||
}
|
||||
|
||||
static GreasePencilBatchCache *grease_pencil_batch_cache_get(GreasePencil &grease_pencil, int cfra)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
if (!grease_pencil_batch_cache_valid(grease_pencil)) {
|
||||
grese_pencil_batch_cache_clear(grease_pencil);
|
||||
return grease_pencil_batch_cache_init(grease_pencil, cfra);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Vertex Buffers
|
||||
* \{ */
|
||||
|
||||
static int count_cyclic_curves(bke::CurvesGeometry &curves)
|
||||
{
|
||||
VArray<bool> cyclic = curves.cyclic();
|
||||
const CommonVArrayInfo info = cyclic.common_info();
|
||||
if (info.type == CommonVArrayInfo::Type::Single) {
|
||||
const bool single = *static_cast<const bool *>(info.data);
|
||||
return single ? curves.curves_num() : 0;
|
||||
}
|
||||
else if (info.type == CommonVArrayInfo::Type::Span) {
|
||||
const Span<bool> span(static_cast<const bool *>(info.data), cyclic.size());
|
||||
return span.count(true);
|
||||
}
|
||||
return threading::parallel_reduce(
|
||||
cyclic.index_range(),
|
||||
2048,
|
||||
0,
|
||||
[&](const IndexRange range, const int init) {
|
||||
/* Alternatively, this could use #materialize to retrieve many values at once. */
|
||||
int sum = init;
|
||||
for (const int64_t i : range) {
|
||||
if (cyclic[i]) {
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
},
|
||||
[&](const int a, const int b) { return a + b; });
|
||||
}
|
||||
|
||||
static void grease_pencil_batches_ensure(GreasePencil &grease_pencil, int cfra)
|
||||
{
|
||||
BLI_assert(grease_pencil.runtime != nullptr);
|
||||
GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
|
||||
grease_pencil.runtime->batch_cache);
|
||||
|
||||
if (cache->vbo != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Should be discarded together. */
|
||||
BLI_assert(cache->vbo == nullptr && cache->ibo == nullptr);
|
||||
BLI_assert(cache->geom_batch == nullptr);
|
||||
|
||||
Hans Goudey
commented
Looks like this can be Looks like this can be `Vector<Array<int>>`, which is better since they don't need to be resized anyway
|
||||
/* First count how many vertices and triangles are needed for the whole object. */
|
||||
int total_points_num = 0;
|
||||
Hans Goudey
commented
What about building a vector of the drawings once, to avoid putting most of the code inside the lambda here and the need to call "foreach_visible_drawing" twice:
What about building a vector of the drawings once, to avoid putting most of the code inside the lambda here and the need to call "foreach_visible_drawing" twice:
```
Vector<const GreasePencilDrawing *> drawings;
grease_pencil.foreach_visible_drawing(
cfra, [&](GreasePencilDrawing &drawing) { drawings.append(&drawing); });
...
Array<Array<int>> verts_start_offsets_per_visible_drawing;
Array<Array<int>> tris_start_offsets_per_visible_drawing;
for (const int drawing_i : drawings.index_range()) {
const GreasePencilDrawing &drawing = *drawings[drawing_i];
...
/* Fill buffers with data. */
for (const int drawing_i : drawings.index_range()) {
const GreasePencilDrawing &drawing = *drawings[drawing_i];
```
|
||||
int total_triangles_num = 0; // TODO
|
||||
grease_pencil.foreach_visible_drawing(
|
||||
cfra, [&](GreasePencilDrawing &drawing, blender::bke::gpencil::Layer &layer) {
|
||||
bke::CurvesGeometry &curves = drawing.geometry.wrap();
|
||||
int num_cyclic = count_cyclic_curves(curves);
|
||||
/* One vertex is stored before and after as padding. Cyclic strokes have one extra
|
||||
* vertex.*/
|
||||
total_points_num += curves.points_num() + num_cyclic + curves.curves_num() * 2;
|
||||
Hans Goudey
commented
More specific/helpful variable names than More specific/helpful variable names than `t` and `v` would be nice here
|
||||
});
|
||||
|
||||
GPUUsageType vbo_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
|
||||
/* Create VBOs. */
|
||||
GPUVertFormat *format = grease_pencil_stroke_format();
|
||||
GPUVertFormat *format_col = grease_pencil_color_format();
|
||||
cache->vbo = GPU_vertbuf_create_with_format_ex(format, vbo_flag);
|
||||
cache->vbo_col = GPU_vertbuf_create_with_format_ex(format_col, vbo_flag);
|
||||
/* Add extra space at the end of the buffer because of quad load. */
|
||||
GPU_vertbuf_data_alloc(cache->vbo, total_points_num + 2);
|
||||
GPU_vertbuf_data_alloc(cache->vbo_col, total_points_num + 2);
|
||||
|
||||
GPUIndexBufBuilder ibo;
|
||||
MutableSpan<GreasePencilStrokeVert> verts = {
|
||||
static_cast<GreasePencilStrokeVert *>(GPU_vertbuf_get_data(cache->vbo)),
|
||||
GPU_vertbuf_get_vertex_len(cache->vbo)};
|
||||
Hans Goudey
commented
Not clear what's going on here-- Not clear what's going on here-- `1 + ... + 1`?
Falk David
commented
Looks like my comment about this got lost somehow. There is one extra vertex before and after each stroke. It's written in this way to make it more clear where that padding is added. Looks like my comment about this got lost somehow. There is one extra vertex before and after each stroke. It's written in this way to make it more clear where that padding is added.
|
||||
MutableSpan<GreasePencilColorVert> cols = {
|
||||
static_cast<GreasePencilColorVert *>(GPU_vertbuf_get_data(cache->vbo_col)),
|
||||
GPU_vertbuf_get_vertex_len(cache->vbo_col)};
|
||||
/* Create IBO. */
|
||||
GPU_indexbuf_init(&ibo, GPU_PRIM_TRIS, total_triangles_num, 0xFFFFFFFFu);
|
||||
|
||||
/* Fill buffers with data. */
|
||||
int v = 0;
|
||||
grease_pencil.foreach_visible_drawing(
|
||||
cfra, [&](GreasePencilDrawing &drawing, blender::bke::gpencil::Layer &layer) {
|
||||
bke::CurvesGeometry &curves = drawing.geometry.wrap();
|
||||
bke::AttributeAccessor attributes = curves.attributes();
|
||||
offset_indices::OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const Span<float3> positions = curves.positions();
|
||||
const VArray<bool> cyclic = curves.cyclic();
|
||||
VArray<float> radii = attributes.lookup_or_default<float>(
|
||||
"radius", ATTR_DOMAIN_POINT, 1.0f);
|
||||
VArray<float> opacities = attributes.lookup_or_default<float>(
|
||||
"opacity", ATTR_DOMAIN_POINT, 1.0f);
|
||||
VArray<int> materials = attributes.lookup_or_default<int>(
|
||||
"material_index", ATTR_DOMAIN_CURVE, -1);
|
||||
|
||||
for (const int curve_i : curves.curves_range()) {
|
||||
IndexRange points = points_by_curve[curve_i];
|
||||
const bool is_cyclic = cyclic[curve_i];
|
||||
const int v_start = v;
|
||||
|
||||
/* First point is not drawn. */
|
||||
v++;
|
||||
|
||||
for (const int point_i : points) {
|
||||
copy_v3_v3(verts[v].pos, positions[point_i]);
|
||||
verts[v].radius = radii[point_i];
|
||||
verts[v].opacity = opacities[point_i];
|
||||
verts[v].point_id = v;
|
||||
verts[v].stroke_id = v_start;
|
||||
verts[v].mat = materials[curve_i] % GPENCIL_MATERIAL_BUFFER_LEN;
|
||||
v++;
|
||||
}
|
||||
|
||||
if (is_cyclic) {
|
||||
copy_v3_v3(verts[v].pos, positions[points.first()]);
|
||||
verts[v].radius = radii[points.first()];
|
||||
verts[v].opacity = opacities[points.first()];
|
||||
verts[v].point_id = v;
|
||||
verts[v].stroke_id = v_start;
|
||||
verts[v].mat = materials[curve_i] % GPENCIL_MATERIAL_BUFFER_LEN;
|
||||
v++;
|
||||
}
|
||||
|
||||
Hans Goudey
commented
Use the Use the `*` overload rather than adding `.varray` at the end
|
||||
/* Last point is not drawn. */
|
||||
v++;
|
||||
}
|
||||
});
|
||||
|
||||
/* Mark last 2 verts as invalid. */
|
||||
verts[total_points_num + 0].mat = -1;
|
||||
verts[total_points_num + 1].mat = -1;
|
||||
/* Also mark first vert as invalid. */
|
||||
verts[0].mat = -1;
|
||||
|
||||
/* Finish the IBO. */
|
||||
Hans Goudey
commented
These These `.as_span()` calls shouldn't be necessary
|
||||
cache->ibo = GPU_indexbuf_build(&ibo);
|
||||
/* Create the batches */
|
||||
cache->geom_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo);
|
||||
/* Allow creation of buffer texture. */
|
||||
GPU_vertbuf_use(cache->vbo);
|
||||
GPU_vertbuf_use(cache->vbo_col);
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
|
||||
GPUBatch *DRW_cache_grease_pencil_get(Object *ob, int cfra)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
|
||||
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
|
||||
grease_pencil_batches_ensure(grease_pencil, cfra);
|
||||
Sergey Sharybin
commented
Add details about TODO. Something that helps others to pick up a task, or to understand that specific case is not expected to be yet working. Add details about TODO. Something that helps others to pick up a task, or to understand that specific case is not expected to be yet working.
|
||||
|
||||
return cache->geom_batch;
|
||||
}
|
||||
|
||||
GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(Object *ob, int cfra)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
|
||||
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
|
||||
grease_pencil_batches_ensure(grease_pencil, cfra);
|
||||
|
||||
return cache->vbo;
|
||||
}
|
||||
|
||||
GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(Object *ob, int cfra)
|
||||
{
|
||||
using namespace blender::draw;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
|
||||
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil, cfra);
|
||||
grease_pencil_batches_ensure(grease_pencil, cfra);
|
||||
|
||||
return cache->vbo_col;
|
||||
}
|
Can those be renamed to
DRW_cache_gpencil_legacy_*
?