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
Notes: blender-bot 2023-02-13 19:39:54 +01:00
Referenced by issue #85423, Crash on toggle bone layer
6 changed files with 200 additions and 47 deletions

View File

@ -39,6 +39,7 @@ struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
struct Curve;
class CurveEval;
enum class GeometryOwnershipType {
@ -406,6 +407,15 @@ class CurveComponent : public GeometryComponent {
CurveEval *curve_ = nullptr;
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:
CurveComponent();
~CurveComponent();
@ -430,6 +440,8 @@ class CurveComponent : public GeometryComponent {
bool owns_direct_data() const override;
void ensure_owns_direct_data() override;
const Curve *get_curve_for_render() const;
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
private:

View File

@ -14,9 +14,14 @@
* 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_math.hh"
#include "BKE_curve.h"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_spline.hh"
#include "attribute_access_intern.hh"
@ -58,6 +63,11 @@ void CurveComponent::clear()
if (ownership_ == GeometryOwnershipType::Owned) {
delete curve_;
}
if (curve_for_render_ != nullptr) {
BKE_id_free(nullptr, curve_for_render_);
curve_for_render_ = 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_;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -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;
return false;
}

View File

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

View File

@ -25,8 +25,11 @@
#include "MEM_guardedalloc.h"
#include "BLI_array.hh"
#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
#include "DNA_curve_types.h"
@ -34,6 +37,8 @@
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_font.h"
#include "BKE_geometry_set.hh"
#include "BKE_spline.hh"
#include "GPU_batch.h"
#include "GPU_capabilities.h"
@ -48,6 +53,11 @@
#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. */
#define SELECT 1
#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)
{
int normal_len = 0;
@ -192,6 +217,9 @@ struct CurveRenderData {
/* borrow from 'Object' */
CurveCache *ob_curve_cache;
/* Owned by the evaluated object's geometry set (#geometry_set_eval). */
const CurveEval *curve_eval;
/* borrow from 'Curve' */
ListBase *nurbs;
@ -230,11 +258,21 @@ static CurveRenderData *curve_render_data_create(Curve *cu,
rdata->ob_curve_cache = ob_curve_cache;
rdata->curve_eval = cu->curve_eval;
if (types & CU_DATATYPE_WIRE) {
curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache,
&rdata->wire.curve_len,
&rdata->wire.vert_len,
&rdata->wire.edge_len);
if (rdata->curve_eval != nullptr) {
curve_eval_render_wire_verts_edges_len_get(*rdata->curve_eval,
&rdata->wire.curve_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) {
@ -556,8 +594,6 @@ void DRW_curve_batch_cache_free(Curve *cu)
/* GPUBatch cache usage. */
static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos)
{
BLI_assert(rdata->ob_curve_cache != nullptr);
static GPUVertFormat format = {0};
static struct {
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_data_alloc(vbo_curves_pos, vert_len);
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]);
if (rdata->curve_eval != nullptr) {
const CurveEval &curve_eval = *rdata->curve_eval;
Span<SplinePtr> splines = curve_eval.splines();
Array<int> offsets = curve_eval.evaluated_point_offsets();
BLI_assert(offsets.last() == vert_len);
for (const int i_spline : splines.index_range()) {
Span<float3> positions = splines[i_spline]->evaluated_positions();
for (const int i_point : positions.index_range()) {
GPU_vertbuf_attr_set(
vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]);
}
}
}
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)
{
BLI_assert(rdata->ob_curve_cache != nullptr);
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 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;
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len);
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_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));
if (rdata->curve_eval != nullptr) {
const CurveEval &curve_eval = *rdata->curve_eval;
Span<SplinePtr> splines = curve_eval.splines();
Array<int> offsets = curve_eval.evaluated_point_offsets();
BLI_assert(offsets.last() == vert_len);
for (const int i_spline : splines.index_range()) {
const int eval_size = splines[i_spline]->evaluated_points_size();
if (splines[i_spline]->is_cyclic()) {
GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 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_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);
}
@ -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);
/* DispLists */
ListBase *lb = &rdata->ob_curve_cache->disp;
/* The object's curve cache can be empty (in one case because we use #CurveEval's cache instead),
* 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 */
if (DRW_vbo_requested(cache->ordered.pos_nor)) {

View File

@ -43,6 +43,7 @@ struct Key;
struct Material;
struct Object;
struct VFont;
struct CurveEval;
/* These two Lines with # tell makesdna this struct can be excluded. */
#
@ -300,6 +301,12 @@ typedef struct Curve {
char _pad2[6];
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;
} Curve;