Geometry Nodes: Draw curve data in the viewport

This patch adds relatively small changes to the curve draw
cache implementation in order to draw the curve data in the
viewport. The dependency graph iterator is also modified
so that it iterates over the curve geometry component, which
is presented to users as `Curve` data with a pointer to the
`CurveEval`

The idea with the spline data type in geometry nodes is that
curve data itself is only the control points, and any evaluated
data with faces is a mesh. That is mostly expected elsewhere in
Blender anyway. This means it's only necessary to implement
wire edge drawing of `CurveEval` data.

Adding a `CurveEval` pointer to `Curve` is in line with changes
I'd like to make in the future like using `CurveEval` in more places
such as edit mode.

An alternate solution involves converting the curve wire data
to a mesh, however, that requires copying all of the data, and
since avoiding it is rather simple and is in-line with future plans
anyway, I think doing it this way is better.

Differential Revision: https://developer.blender.org/D11351
This commit is contained in:
Hans Goudey 2021-05-27 10:08:40 -04:00
parent 5621a8ed7f
commit ac833108db
6 changed files with 200 additions and 47 deletions

@ -39,6 +39,7 @@ struct Mesh;
struct Object; struct Object;
struct PointCloud; struct PointCloud;
struct Volume; struct Volume;
struct Curve;
class CurveEval; class CurveEval;
enum class GeometryOwnershipType { enum class GeometryOwnershipType {
@ -406,6 +407,15 @@ class CurveComponent : public GeometryComponent {
CurveEval *curve_ = nullptr; CurveEval *curve_ = nullptr;
GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
/**
* Curve data necessary to hold the draw cache for rendering, consistent over multiple redraws.
* This is necessary because Blender assumes that objects evaluate to an object data type, and
* we use #CurveEval rather than #Curve here. It also allows us to mostly reuse the same
* batch cache implementation.
*/
mutable Curve *curve_for_render_ = nullptr;
mutable std::mutex curve_for_render_mutex_;
public: public:
CurveComponent(); CurveComponent();
~CurveComponent(); ~CurveComponent();
@ -430,6 +440,8 @@ class CurveComponent : public GeometryComponent {
bool owns_direct_data() const override; bool owns_direct_data() const override;
void ensure_owns_direct_data() override; void ensure_owns_direct_data() override;
const Curve *get_curve_for_render() const;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
private: private:

@ -14,9 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
#include "DNA_ID_enums.h"
#include "DNA_curve_types.h"
#include "BKE_attribute_access.hh" #include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh" #include "BKE_attribute_math.hh"
#include "BKE_curve.h"
#include "BKE_geometry_set.hh" #include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_spline.hh" #include "BKE_spline.hh"
#include "attribute_access_intern.hh" #include "attribute_access_intern.hh"
@ -58,6 +63,11 @@ void CurveComponent::clear()
if (ownership_ == GeometryOwnershipType::Owned) { if (ownership_ == GeometryOwnershipType::Owned) {
delete curve_; delete curve_;
} }
if (curve_for_render_ != nullptr) {
BKE_id_free(nullptr, curve_for_render_);
curve_for_render_ = nullptr;
}
curve_ = nullptr; curve_ = nullptr;
} }
} }
@ -118,6 +128,29 @@ void CurveComponent::ensure_owns_direct_data()
} }
} }
/**
* Create empty curve data used for rendering the spline's wire edges.
* \note See comment on #curve_for_render_ for further explanation.
*/
const Curve *CurveComponent::get_curve_for_render() const
{
if (curve_ == nullptr) {
return nullptr;
}
if (curve_for_render_ != nullptr) {
return curve_for_render_;
}
std::lock_guard lock{curve_for_render_mutex_};
if (curve_for_render_ != nullptr) {
return curve_for_render_;
}
curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU, nullptr);
curve_for_render_->curve_eval = curve_;
return curve_for_render_;
}
/** \} */ /** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */

@ -219,6 +219,29 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
} }
} }
/* The curve component. */
if (data->geometry_component_id == 3) {
data->geometry_component_id++;
const CurveComponent *component = geometry_set->get_component_for_read<CurveComponent>();
if (component != nullptr) {
const Curve *curve = component->get_curve_for_render();
if (curve != nullptr) {
Object *temp_object = &data->temp_geometry_component_object;
*temp_object = *data->geometry_component_owner;
temp_object->type = OB_CURVE;
temp_object->data = (void *)curve;
/* Assign data_eval here too, because curve rendering code tries
* to use a mesh if it can find one in this pointer. */
temp_object->runtime.data_eval = (ID *)curve;
temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id;
iter->current = temp_object;
return true;
}
}
}
data->geometry_component_owner = nullptr; data->geometry_component_owner = nullptr;
return false; return false;
} }

@ -31,6 +31,7 @@ set(INC
../depsgraph ../depsgraph
../editors/include ../editors/include
../editors/space_view3d ../editors/space_view3d
../functions
../gpu ../gpu
../imbuf ../imbuf
../makesdna ../makesdna

@ -25,8 +25,11 @@
#include "MEM_guardedalloc.h" #include "MEM_guardedalloc.h"
#include "BLI_array.hh"
#include "BLI_float3.hh"
#include "BLI_listbase.h" #include "BLI_listbase.h"
#include "BLI_math_vector.h" #include "BLI_math_vector.h"
#include "BLI_span.hh"
#include "BLI_utildefines.h" #include "BLI_utildefines.h"
#include "DNA_curve_types.h" #include "DNA_curve_types.h"
@ -34,6 +37,8 @@
#include "BKE_curve.h" #include "BKE_curve.h"
#include "BKE_displist.h" #include "BKE_displist.h"
#include "BKE_font.h" #include "BKE_font.h"
#include "BKE_geometry_set.hh"
#include "BKE_spline.hh"
#include "GPU_batch.h" #include "GPU_batch.h"
#include "GPU_capabilities.h" #include "GPU_capabilities.h"
@ -48,6 +53,11 @@
#include "draw_cache_impl.h" /* own include */ #include "draw_cache_impl.h" /* own include */
using blender::Array;
using blender::float3;
using blender::IndexRange;
using blender::Span;
/* See: edit_curve_point_vert.glsl for duplicate includes. */ /* See: edit_curve_point_vert.glsl for duplicate includes. */
#define SELECT 1 #define SELECT 1
#define ACTIVE_NURB (1 << 2) #define ACTIVE_NURB (1 << 2)
@ -139,6 +149,21 @@ static void curve_render_wire_verts_edges_len_get(const CurveCache *ob_curve_cac
} }
} }
static void curve_eval_render_wire_verts_edges_len_get(const CurveEval &curve_eval,
int *r_curve_len,
int *r_vert_len,
int *r_edge_len)
{
Span<SplinePtr> splines = curve_eval.splines();
*r_curve_len = splines.size();
*r_vert_len = 0;
*r_edge_len = 0;
for (const SplinePtr &spline : splines) {
*r_vert_len += spline->evaluated_points_size();
*r_edge_len += spline->evaluated_edges_size();
}
}
static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_curve_cache) static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_curve_cache)
{ {
int normal_len = 0; int normal_len = 0;
@ -192,6 +217,9 @@ struct CurveRenderData {
/* borrow from 'Object' */ /* borrow from 'Object' */
CurveCache *ob_curve_cache; CurveCache *ob_curve_cache;
/* Owned by the evaluated object's geometry set (#geometry_set_eval). */
const CurveEval *curve_eval;
/* borrow from 'Curve' */ /* borrow from 'Curve' */
ListBase *nurbs; ListBase *nurbs;
@ -230,11 +258,21 @@ static CurveRenderData *curve_render_data_create(Curve *cu,
rdata->ob_curve_cache = ob_curve_cache; rdata->ob_curve_cache = ob_curve_cache;
rdata->curve_eval = cu->curve_eval;
if (types & CU_DATATYPE_WIRE) { if (types & CU_DATATYPE_WIRE) {
curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache, if (rdata->curve_eval != nullptr) {
&rdata->wire.curve_len, curve_eval_render_wire_verts_edges_len_get(*rdata->curve_eval,
&rdata->wire.vert_len, &rdata->wire.curve_len,
&rdata->wire.edge_len); &rdata->wire.vert_len,
&rdata->wire.edge_len);
}
else {
curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache,
&rdata->wire.curve_len,
&rdata->wire.vert_len,
&rdata->wire.edge_len);
}
} }
if (cu->editnurb) { if (cu->editnurb) {
@ -556,8 +594,6 @@ void DRW_curve_batch_cache_free(Curve *cu)
/* GPUBatch cache usage. */ /* GPUBatch cache usage. */
static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos) static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos)
{ {
BLI_assert(rdata->ob_curve_cache != nullptr);
static GPUVertFormat format = {0}; static GPUVertFormat format = {0};
static struct { static struct {
uint pos; uint pos;
@ -570,30 +606,46 @@ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curv
GPU_vertbuf_init_with_format(vbo_curves_pos, &format); GPU_vertbuf_init_with_format(vbo_curves_pos, &format);
GPU_vertbuf_data_alloc(vbo_curves_pos, vert_len); GPU_vertbuf_data_alloc(vbo_curves_pos, vert_len);
int v_idx = 0; if (rdata->curve_eval != nullptr) {
LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { const CurveEval &curve_eval = *rdata->curve_eval;
if (bl->nr <= 0) { Span<SplinePtr> splines = curve_eval.splines();
continue; Array<int> offsets = curve_eval.evaluated_point_offsets();
} BLI_assert(offsets.last() == vert_len);
const int i_end = v_idx + bl->nr;
for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) { for (const int i_spline : splines.index_range()) {
GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec); Span<float3> positions = splines[i_spline]->evaluated_positions();
} for (const int i_point : positions.index_range()) {
} GPU_vertbuf_attr_set(
LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]);
if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
for (int i = 0; i < dl->nr; v_idx++, i++) {
GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]);
} }
} }
} }
BLI_assert(v_idx == vert_len); else {
BLI_assert(rdata->ob_curve_cache != nullptr);
int v_idx = 0;
LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
if (bl->nr <= 0) {
continue;
}
const int i_end = v_idx + bl->nr;
for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) {
GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec);
}
}
LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
for (int i = 0; i < dl->nr; v_idx++, i++) {
GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]);
}
}
}
BLI_assert(v_idx == vert_len);
}
} }
static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines) static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines)
{ {
BLI_assert(rdata->ob_curve_cache != nullptr);
const int vert_len = curve_render_data_wire_verts_len_get(rdata); const int vert_len = curve_render_data_wire_verts_len_get(rdata);
const int edge_len = curve_render_data_wire_edges_len_get(rdata); const int edge_len = curve_render_data_wire_edges_len_get(rdata);
const int curve_len = curve_render_data_wire_curve_len_get(rdata); const int curve_len = curve_render_data_wire_curve_len_get(rdata);
@ -603,34 +655,56 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c
GPUIndexBufBuilder elb; GPUIndexBufBuilder elb;
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len); GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len);
int v_idx = 0; if (rdata->curve_eval != nullptr) {
LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { const CurveEval &curve_eval = *rdata->curve_eval;
if (bl->nr <= 0) { Span<SplinePtr> splines = curve_eval.splines();
continue; Array<int> offsets = curve_eval.evaluated_point_offsets();
} BLI_assert(offsets.last() == vert_len);
const bool is_cyclic = bl->poly != -1;
if (is_cyclic) { for (const int i_spline : splines.index_range()) {
GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1)); const int eval_size = splines[i_spline]->evaluated_points_size();
} if (splines[i_spline]->is_cyclic()) {
for (int i = 0; i < bl->nr; i++) { GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1);
GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
}
GPU_indexbuf_add_primitive_restart(&elb);
v_idx += bl->nr;
}
LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
const bool is_cyclic = dl->type == DL_POLY;
if (is_cyclic) {
GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1));
} }
for (int i = 0; i < dl->nr; i++) { for (const int i_point : IndexRange(eval_size)) {
GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + i_point);
}
GPU_indexbuf_add_primitive_restart(&elb);
}
}
else {
BLI_assert(rdata->ob_curve_cache != nullptr);
int v_idx = 0;
LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
if (bl->nr <= 0) {
continue;
}
const bool is_cyclic = bl->poly != -1;
if (is_cyclic) {
GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1));
}
for (int i = 0; i < bl->nr; i++) {
GPU_indexbuf_add_generic_vert(&elb, v_idx + i); GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
} }
GPU_indexbuf_add_primitive_restart(&elb); GPU_indexbuf_add_primitive_restart(&elb);
v_idx += dl->nr; v_idx += bl->nr;
}
LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
const bool is_cyclic = dl->type == DL_POLY;
if (is_cyclic) {
GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1));
}
for (int i = 0; i < dl->nr; i++) {
GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
}
GPU_indexbuf_add_primitive_restart(&elb);
v_idx += dl->nr;
}
} }
} }
GPU_indexbuf_build_in_place(&elb, ibo_curve_lines); GPU_indexbuf_build_in_place(&elb, ibo_curve_lines);
} }
@ -1070,8 +1144,11 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen
CurveRenderData *rdata = curve_render_data_create(cu, ob->runtime.curve_cache, mr_flag); CurveRenderData *rdata = curve_render_data_create(cu, ob->runtime.curve_cache, mr_flag);
/* DispLists */ /* The object's curve cache can be empty (in one case because we use #CurveEval's cache instead),
ListBase *lb = &rdata->ob_curve_cache->disp; * If so, point to an empty DispList list to avoid the need to check for null in the following
* functions. */
ListBase empty_lb = {nullptr, nullptr};
ListBase *lb = rdata->ob_curve_cache == nullptr ? &empty_lb : &rdata->ob_curve_cache->disp;
/* Generate VBOs */ /* Generate VBOs */
if (DRW_vbo_requested(cache->ordered.pos_nor)) { if (DRW_vbo_requested(cache->ordered.pos_nor)) {

@ -43,6 +43,7 @@ struct Key;
struct Material; struct Material;
struct Object; struct Object;
struct VFont; struct VFont;
struct CurveEval;
/* These two Lines with # tell makesdna this struct can be excluded. */ /* These two Lines with # tell makesdna this struct can be excluded. */
# #
@ -300,6 +301,12 @@ typedef struct Curve {
char _pad2[6]; char _pad2[6];
float fsize_realtime; float fsize_realtime;
/**
* A pointer to curve data from geometry nodes, currently only set for evaluated
* objects by the dependency graph iterator, and owned by #geometry_set_eval.
*/
struct CurveEval *curve_eval;
void *batch_cache; void *batch_cache;
} Curve; } Curve;