Curves: draw evaluated curves and handles in edit mode

This makes the edit mode drawing for the new curves data more similar
to the old edit mode. Specifically, it draws the evaluated curves now instead
of just a poly curve. Furthermore, it now draws bezier handles as well as
a separate control curve for nurbs curves.

Pull Request: https://projects.blender.org/blender/blender/pulls/119053
This commit is contained in:
laurynas 2024-03-19 10:39:05 +01:00 committed by Jacques Lucke
parent 1ef6d1a11b
commit 15dbfe54e4
12 changed files with 494 additions and 74 deletions

@ -190,6 +190,18 @@ inline void gather_group_to_group(const OffsetIndices<int> src_offsets,
});
}
template<typename T>
inline void gather_group_to_group(const OffsetIndices<int> src_offsets,
const OffsetIndices<int> dst_offsets,
const IndexMask &selection,
const VArray<T> src,
MutableSpan<T> dst)
{
selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
src.materialize_compressed(src_offsets[src_i], dst.slice(dst_offsets[dst_i]));
});
}
template<typename T>
inline void gather_to_groups(const OffsetIndices<int> dst_offsets,
const IndexMask &src_selection,

@ -762,6 +762,8 @@ set(GLSL_SRC
engines/overlay/shaders/overlay_edit_curve_handle_vert.glsl
engines/overlay/shaders/overlay_edit_curve_handle_vert_no_geom.glsl
engines/overlay/shaders/overlay_edit_curve_point_vert.glsl
engines/overlay/shaders/overlay_edit_curves_handle_frag.glsl
engines/overlay/shaders/overlay_edit_curves_handle_vert.glsl
engines/overlay/shaders/overlay_edit_curve_wire_vert.glsl
engines/overlay/shaders/overlay_edit_gpencil_canvas_vert.glsl
engines/overlay/shaders/overlay_edit_gpencil_guide_vert.glsl

@ -56,13 +56,19 @@ void OVERLAY_edit_curves_cache_init(OVERLAY_Data *vedata)
grp = pd->edit_curves_points_grp[i] = DRW_shgroup_create(sh, psl->edit_curves_points_ps[i]);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
}
DRW_PASS_CREATE(psl->edit_curves_lines_ps[i], (state | pd->clipping_state));
sh = OVERLAY_shader_edit_particle_strand();
grp = pd->edit_curves_lines_grp[i] = DRW_shgroup_create(sh, psl->edit_curves_lines_ps[i]);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_bool_copy(grp, "useWeight", false);
}
{
state = DRW_STATE_WRITE_COLOR;
DRW_PASS_CREATE(psl->edit_curves_handles_ps, (state | pd->clipping_state));
sh = OVERLAY_shader_edit_curves_handle();
grp = pd->edit_curves_handles_grp = DRW_shgroup_create(sh, psl->edit_curves_handles_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
}
}
static void overlay_edit_curves_add_ob_to_pass(OVERLAY_PrivateData *pd, Object *ob, bool in_front)
@ -76,8 +82,16 @@ static void overlay_edit_curves_add_ob_to_pass(OVERLAY_PrivateData *pd, Object *
DRW_shgroup_call_no_cull(point_shgrp, geom_points, ob);
}
DRWShadingGroup *handles_shgrp = pd->edit_curves_handles_grp;
DRW_shgroup_uniform_block(
handles_shgrp, "curvesInfoBlock", DRW_curves_batch_cache_ubo_storage(curves));
GPUBatch *geom_handles = DRW_curves_batch_cache_get_edit_curves_handles(curves);
DRW_shgroup_call_no_cull(handles_shgrp, geom_handles, ob);
DRWShadingGroup *lines_shgrp = pd->edit_curves_lines_grp[in_front];
GPUBatch *geom_lines = DRW_curves_batch_cache_get_edit_lines(curves);
DRW_shgroup_uniform_block(
lines_shgrp, "curvesInfoBlock", DRW_curves_batch_cache_ubo_storage(curves));
GPUBatch *geom_lines = DRW_curves_batch_cache_get_edit_curves_lines(curves);
DRW_shgroup_call_no_cull(lines_shgrp, geom_lines, ob);
}
@ -85,12 +99,7 @@ void OVERLAY_edit_curves_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
if (pd->edit_curves.do_zbufclip) {
overlay_edit_curves_add_ob_to_pass(pd, ob, false);
}
else {
overlay_edit_curves_add_ob_to_pass(pd, ob, true);
}
overlay_edit_curves_add_ob_to_pass(pd, ob, !pd->edit_curves.do_zbufclip);
}
void OVERLAY_edit_curves_draw(OVERLAY_Data *vedata)
@ -103,18 +112,13 @@ void OVERLAY_edit_curves_draw(OVERLAY_Data *vedata)
GPU_framebuffer_bind(fbl->overlay_default_fb);
}
if (pd->edit_curves.do_zbufclip) {
DRW_view_set_active(pd->view_edit_curves);
if (pd->edit_curves.do_points) {
DRW_draw_pass(psl->edit_curves_points_ps[NOT_IN_FRONT]);
}
DRW_draw_pass(psl->edit_curves_lines_ps[NOT_IN_FRONT]);
}
else {
DRW_view_set_active(pd->view_edit_curves);
if (pd->edit_curves.do_points) {
DRW_draw_pass(psl->edit_curves_points_ps[IN_FRONT]);
}
DRW_draw_pass(psl->edit_curves_lines_ps[IN_FRONT]);
int in_front = pd->edit_curves.do_zbufclip ? NOT_IN_FRONT : IN_FRONT;
DRW_view_set_active(pd->view_edit_curves);
DRW_draw_pass(psl->edit_curves_lines_ps[in_front]);
DRW_draw_pass(psl->edit_curves_handles_ps);
if (pd->edit_curves.do_points) {
DRW_draw_pass(psl->edit_curves_points_ps[in_front]);
}
}

@ -81,6 +81,7 @@ struct OVERLAY_PassList {
DRWPass *edit_mesh_faces_ps[2];
DRWPass *edit_mesh_faces_cage_ps[2];
DRWPass *edit_curves_points_ps[2];
DRWPass *edit_curves_handles_ps;
DRWPass *edit_curves_lines_ps[2];
DRWPass *edit_mesh_analysis_ps;
DRWPass *edit_mesh_normals_ps;
@ -276,6 +277,7 @@ struct OVERLAY_PrivateData {
DRWShadingGroup *edit_uv_face_dots_grp;
DRWShadingGroup *edit_uv_stretching_grp;
DRWShadingGroup *edit_curves_points_grp[2];
DRWShadingGroup *edit_curves_handles_grp;
DRWShadingGroup *edit_curves_lines_grp[2];
DRWShadingGroup *extra_grid_grp;
DRWShadingGroup *facing_grp[2];
@ -737,6 +739,7 @@ GPUShader *OVERLAY_shader_depth_only();
GPUShader *OVERLAY_shader_edit_curve_handle();
GPUShader *OVERLAY_shader_edit_curve_point();
GPUShader *OVERLAY_shader_edit_curve_wire();
GPUShader *OVERLAY_shader_edit_curves_handle();
GPUShader *OVERLAY_shader_edit_gpencil_guide_point();
GPUShader *OVERLAY_shader_edit_gpencil_point();
GPUShader *OVERLAY_shader_edit_gpencil_wire();

@ -94,7 +94,7 @@ static void populate_edit_overlay(OVERLAY_Data *vedata, Object *object)
OVERLAY_PrivateData *pd = vedata->stl->pd;
Curves *curves = static_cast<Curves *>(object->data);
GPUBatch *geom_lines = DRW_curves_batch_cache_get_edit_lines(curves);
GPUBatch *geom_lines = DRW_curves_batch_cache_get_edit_curves_handles(curves);
DRW_shgroup_call_no_cull(pd->sculpt_curves_cage_lines_grp, geom_lines, object);
}

@ -34,6 +34,7 @@ struct OVERLAY_Shaders {
GPUShader *depth_only;
GPUShader *edit_curve_handle;
GPUShader *edit_curve_point;
GPUShader *edit_curves_handle;
GPUShader *edit_curve_wire;
GPUShader *edit_gpencil_guide_point;
GPUShader *edit_gpencil_point;
@ -332,6 +333,18 @@ GPUShader *OVERLAY_shader_edit_curve_point()
return sh_data->edit_curve_point;
}
GPUShader *OVERLAY_shader_edit_curves_handle()
{
const DRWContextState *draw_ctx = DRW_context_state_get();
OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
if (!sh_data->edit_curves_handle) {
sh_data->edit_curves_handle = GPU_shader_create_from_info_name(
(draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) ? "overlay_edit_curves_handle_clipped" :
"overlay_edit_curves_handle");
}
return sh_data->edit_curves_handle;
}
GPUShader *OVERLAY_shader_edit_curve_wire()
{
const DRWContextState *draw_ctx = DRW_context_state_get();

@ -62,6 +62,11 @@ struct OVERLAY_GridData {
BLI_STATIC_ASSERT_ALIGN(OVERLAY_GridData, 16)
#ifdef GPU_SHADER
/* Keep the same values as in `draw_cache_impl_curves.cc` */
# define EDIT_CURVES_NURBS_CONTROL_POINT (1u)
# define EDIT_CURVES_BEZIER_HANDLE (1u << 1)
# define EDIT_CURVES_LEFT_HANDLE_TYPES_SHIFT (6u)
# define EDIT_CURVES_RIGHT_HANDLE_TYPES_SHIFT (4u)
/* Keep the same values as in `draw_cache_imp_curve.c` */
# define ACTIVE_NURB (1u << 2)
# define BEZIER_HANDLE (1u << 3)

@ -489,7 +489,30 @@ GPU_SHADER_CREATE_INFO(overlay_edit_curve_wire_clipped)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edit Curve
/** \name Edit Curves
* \{ */
GPU_SHADER_CREATE_INFO(overlay_edit_curves_handle)
.do_static_compilation(true)
.typedef_source("overlay_shader_shared.h")
.vertex_in(0, Type::VEC3, "pos")
.vertex_in(1, Type::UINT, "data")
.vertex_in(2, Type::FLOAT, "selection")
.vertex_out(overlay_edit_smooth_color_iface.smooth(Type::VEC4, "leftColor"))
.uniform_buf(0, "int", "curvesInfoBlock[4]")
.fragment_out(0, Type::VEC4, "fragColor")
.vertex_source("overlay_edit_curves_handle_vert.glsl")
.fragment_source("overlay_edit_curves_handle_frag.glsl")
.additional_info("draw_mesh", "draw_globals");
GPU_SHADER_CREATE_INFO(overlay_edit_curves_handle_clipped)
.do_static_compilation(true)
.additional_info("overlay_edit_curves_handle", "drw_clipped");
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edit Lattice
* \{ */
GPU_SHADER_CREATE_INFO(overlay_edit_lattice_point)

@ -0,0 +1,14 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void main()
{
/* In the layout of index buffer for curves handles is:
* [ left bezier handles, right bezier handles, NURBS handles].
* So first bezier_point_count lines will use leftColor. All other will be using finalColor as
* vertex shader stores right handles color in finalColor variable.
*/
int bezier_point_count = curvesInfoBlock[0];
fragColor = gl_PrimitiveID < bezier_point_count ? leftColor : finalColor;
}

@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
float4 get_bezier_handle_color(uint color_id, float sel)
{
switch (color_id) {
case 0u: /* BEZIER_HANDLE_FREE */
return mix(globalsBlock.color_handle_free, globalsBlock.color_handle_sel_free, sel);
case 1u: /* BEZIER_HANDLE_AUTO */
return mix(globalsBlock.color_handle_auto, globalsBlock.color_handle_sel_auto, sel);
case 2u: /* BEZIER_HANDLE_VECTOR */
return mix(globalsBlock.color_handle_vect, globalsBlock.color_handle_sel_vect, sel);
case 3u: /* BEZIER_HANDLE_ALIGN */
return mix(globalsBlock.color_handle_align, globalsBlock.color_handle_sel_align, sel);
}
return mix(globalsBlock.color_handle_autoclamp, globalsBlock.color_handle_sel_autoclamp, sel);
}
void main()
{
GPU_INTEL_VERTEX_SHADER_WORKAROUND
vec3 world_pos = point_object_to_world(pos);
gl_Position = point_world_to_ndc(world_pos);
if ((data & EDIT_CURVES_BEZIER_HANDLE) != 0u) {
leftColor = get_bezier_handle_color((data >> EDIT_CURVES_LEFT_HANDLE_TYPES_SHIFT) & 3,
selection);
finalColor = get_bezier_handle_color((data >> EDIT_CURVES_RIGHT_HANDLE_TYPES_SHIFT) & 3,
selection);
}
else if ((data & EDIT_CURVES_NURBS_CONTROL_POINT) != 0u) {
finalColor = mix(globalsBlock.color_nurb_uline, globalsBlock.color_nurb_sel_uline, selection);
}
else {
finalColor = mix(colorWire, colorVertexSelect, selection);
}
view_clipping_distances(world_pos);
}

@ -11,6 +11,7 @@
struct GPUBatch;
struct GPUMaterial;
struct GPUVertBuf;
struct GPUUniformBuf;
struct ModifierData;
struct PTCacheEdit;
struct ParticleSystem;
@ -135,8 +136,10 @@ GPUVertBuf **DRW_curves_texture_for_evaluated_attribute(Curves *curves,
const char *name,
bool *r_is_point_domain);
GPUUniformBuf *DRW_curves_batch_cache_ubo_storage(Curves *curves);
GPUBatch *DRW_curves_batch_cache_get_edit_points(Curves *curves);
GPUBatch *DRW_curves_batch_cache_get_edit_lines(Curves *curves);
GPUBatch *DRW_curves_batch_cache_get_edit_curves_handles(Curves *curves);
GPUBatch *DRW_curves_batch_cache_get_edit_curves_lines(Curves *curves);
void DRW_curves_batch_cache_create_requested(Object *ob);

@ -12,8 +12,10 @@
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.hh"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_span.hh"
@ -28,6 +30,7 @@
#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
#include "BKE_curves_utils.hh"
#include "BKE_customdata.hh"
#include "BKE_geometry_set.hh"
@ -45,22 +48,53 @@
namespace blender::draw {
#define EDIT_CURVES_NURBS_CONTROL_POINT (1u)
#define EDIT_CURVES_BEZIER_HANDLE (1u << 1)
#define EDIT_CURVES_LEFT_HANDLE_TYPES_SHIFT (6u)
#define EDIT_CURVES_RIGHT_HANDLE_TYPES_SHIFT (4u)
/* ---------------------------------------------------------------------- */
struct CurvesUboStorage {
int32_t bezier_point_count;
float _pad1, _pad2, _pad3;
};
/* Curves GPUBatch Cache */
struct CurvesBatchCache {
CurvesEvalCache curves_cache;
GPUBatch *edit_points;
GPUBatch *edit_lines;
GPUBatch *edit_handles;
/* Crazy-space point positions for original points. */
GPUVertBuf *edit_points_pos;
/* Additional data needed for shader to choose color for each point in edit_points_pos.
* If first bit is set, then point is NURBS control point. EDIT_CURVES_NURBS_CONTROL_POINT is
* used to set and test. If second, then point is Bezier handle point. Set and tested with
* EDIT_CURVES_BEZIER_HANDLE.
* In Bezier case two handle types of HandleType are also encoded.
* Byte structure for Bezier knot point (handle middle point):
* | left handle type | right handle type | | BEZIER| NURBS|
* | 7 6 | 5 4 | 3 2 | 1 | 0 |
*
* If it is left or right handle point, then same handle type is repeated in both slots.
*/
GPUVertBuf *edit_points_data;
/* Buffer used to store CurvesUboStorage value. push_constant() could not be used for this
* value, as it is not know in overlay_edit_curves.cc as other constants. */
GPUUniformBuf *curves_ubo_storage;
/* Selection of original points. */
GPUVertBuf *edit_points_selection;
GPUIndexBuf *edit_lines_ibo;
GPUIndexBuf *edit_handles_ibo;
GPUBatch *edit_curves_lines;
GPUVertBuf *edit_curves_lines_pos;
GPUIndexBuf *edit_curves_lines_ibo;
/* Whether the cache is invalid. */
bool is_dirty;
@ -73,6 +107,19 @@ struct CurvesBatchCache {
std::mutex render_mutex;
};
static uint DUMMY_ID;
static GPUVertFormat single_attr_vertbuffer_format(const char *name,
GPUVertCompType comp_type,
uint comp_len,
GPUVertFetchMode fetch_mode,
uint &attr_id = DUMMY_ID)
{
GPUVertFormat format{};
attr_id = GPU_vertformat_attr_add(&format, name, comp_type, comp_len, fetch_mode);
return format;
}
static bool curves_batch_cache_valid(const Curves &curves)
{
const CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache);
@ -85,6 +132,8 @@ static void curves_batch_cache_init(Curves &curves)
if (!cache) {
cache = MEM_new<CurvesBatchCache>(__func__);
cache->curves_ubo_storage = GPU_uniformbuf_create_ex(
sizeof(CurvesUboStorage), nullptr, "CurvesUboStorage");
curves.batch_cache = cache;
}
else {
@ -113,11 +162,16 @@ static void curves_batch_cache_clear_edit_data(CurvesBatchCache *cache)
{
/* TODO: more granular update tagging. */
GPU_VERTBUF_DISCARD_SAFE(cache->edit_points_pos);
GPU_VERTBUF_DISCARD_SAFE(cache->edit_points_data);
GPU_VERTBUF_DISCARD_SAFE(cache->edit_points_selection);
GPU_INDEXBUF_DISCARD_SAFE(cache->edit_lines_ibo);
GPU_INDEXBUF_DISCARD_SAFE(cache->edit_handles_ibo);
GPU_BATCH_DISCARD_SAFE(cache->edit_points);
GPU_BATCH_DISCARD_SAFE(cache->edit_lines);
GPU_BATCH_DISCARD_SAFE(cache->edit_handles);
GPU_VERTBUF_DISCARD_SAFE(cache->edit_curves_lines_pos);
GPU_INDEXBUF_DISCARD_SAFE(cache->edit_curves_lines_ibo);
GPU_BATCH_DISCARD_SAFE(cache->edit_curves_lines);
}
static void curves_batch_cache_clear_eval_data(CurvesEvalCache &curves_cache)
@ -240,60 +294,206 @@ static void curves_batch_cache_ensure_procedural_pos(const bke::CurvesGeometry &
}
}
static void curves_batch_cache_ensure_edit_points_pos(const bke::CurvesGeometry &curves,
Span<float3> deformed_positions,
CurvesBatchCache &cache)
static uint32_t bezier_data_value(int8_t left_handle_type, int8_t right_handle_type)
{
static GPUVertFormat format_pos = {0};
static uint pos;
if (format_pos.attr_len == 0) {
pos = GPU_vertformat_attr_add(&format_pos, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
}
GPU_vertbuf_init_with_format(cache.edit_points_pos, &format_pos);
GPU_vertbuf_data_alloc(cache.edit_points_pos, curves.points_num());
GPU_vertbuf_attr_fill(cache.edit_points_pos, pos, deformed_positions.data());
return (left_handle_type << EDIT_CURVES_LEFT_HANDLE_TYPES_SHIFT) |
(right_handle_type << EDIT_CURVES_RIGHT_HANDLE_TYPES_SHIFT) | EDIT_CURVES_BEZIER_HANDLE;
}
static void curves_batch_cache_ensure_edit_points_selection(const bke::CurvesGeometry &curves,
CurvesBatchCache &cache)
static uint32_t bezier_data_value(int8_t handle_type)
{
static GPUVertFormat format_data = {0};
if (format_data.attr_len == 0) {
GPU_vertformat_attr_add(&format_data, "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
return bezier_data_value(handle_type, handle_type);
}
static void curves_batch_cache_ensure_edit_points_pos_and_data(
const bke::CurvesGeometry &curves,
const IndexMask bezier_curves,
const OffsetIndices<int> bezier_dst_offsets,
const bke::crazyspace::GeometryDeformation deformation,
CurvesBatchCache &cache)
{
static GPUVertFormat format_pos = single_attr_vertbuffer_format(
"pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
/* GPU_COMP_U32 is used instead of GPU_COMP_U8 because depending on running hardware stride might
* still be 4. Thus adding complexity to the code and still sparing no memory. */
static GPUVertFormat format_data = single_attr_vertbuffer_format(
"data", GPU_COMP_U32, 1, GPU_FETCH_INT);
Span<float3> deformed_positions = deformation.positions;
const int bezier_point_count = bezier_dst_offsets.total_size();
const int size = deformed_positions.size() + bezier_point_count * 2;
GPU_vertbuf_init_with_format(cache.edit_points_pos, &format_pos);
GPU_vertbuf_data_alloc(cache.edit_points_pos, size);
GPU_vertbuf_init_with_format(cache.edit_points_data, &format_data);
GPU_vertbuf_data_alloc(cache.edit_points_data, size);
float3 *pos_buffer_data = static_cast<float3 *>(GPU_vertbuf_get_data(cache.edit_points_pos));
uint32_t *data_buffer_data = static_cast<uint32_t *>(
GPU_vertbuf_get_data(cache.edit_points_data));
MutableSpan<float3> pos_dst(pos_buffer_data, deformed_positions.size());
pos_dst.copy_from(deformed_positions);
MutableSpan<uint32_t> data_dst(data_buffer_data, size);
MutableSpan<uint32_t> handle_data_left(data_buffer_data + deformed_positions.size(),
bezier_point_count);
MutableSpan<uint32_t> handle_data_right(
data_buffer_data + deformed_positions.size() + bezier_point_count, bezier_point_count);
const Span<float3> left_handle_positions = curves.handle_positions_left();
const Span<float3> right_handle_positions = curves.handle_positions_right();
const VArray<int8_t> left_handle_types = curves.handle_types_left();
const VArray<int8_t> right_handle_types = curves.handle_types_right();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
auto handle_other_curves = [&](const uint32_t fill_value) {
return [&, fill_value](const IndexMask &selection) {
selection.foreach_index(GrainSize(256), [&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i];
data_dst.slice(points).fill(fill_value);
});
};
};
bke::curves::foreach_curve_by_type(
curves.curve_types(),
curves.curve_type_counts(),
curves.curves_range(),
handle_other_curves(0),
handle_other_curves(0),
[&](const IndexMask &selection) {
selection.foreach_index(GrainSize(256), [&](const int src_i, const int64_t dst_i) {
for (const int point : points_by_curve[src_i]) {
const int point_in_curve = point - points_by_curve[src_i].start();
const int dst_index = bezier_dst_offsets[dst_i].start() + point_in_curve;
data_dst[point] = bezier_data_value(left_handle_types[point],
right_handle_types[point]);
handle_data_left[dst_index] = bezier_data_value(left_handle_types[point]);
handle_data_right[dst_index] = bezier_data_value(right_handle_types[point]);
}
});
},
handle_other_curves(EDIT_CURVES_NURBS_CONTROL_POINT));
if (!bezier_point_count) {
return;
}
MutableSpan<float3> left_handles(pos_buffer_data + deformed_positions.size(),
bezier_point_count);
MutableSpan<float3> right_handles(
pos_buffer_data + deformed_positions.size() + bezier_point_count, bezier_point_count);
/* TODO: Use deformed left_handle_positions and left_handle_positions. */
array_utils::gather_group_to_group(
points_by_curve, bezier_dst_offsets, bezier_curves, left_handle_positions, left_handles);
array_utils::gather_group_to_group(
points_by_curve, bezier_dst_offsets, bezier_curves, right_handle_positions, right_handles);
}
static void curves_batch_cache_ensure_edit_points_selection(
const bke::CurvesGeometry &curves,
const IndexMask bezier_curves,
const OffsetIndices<int> bezier_dst_offsets,
CurvesBatchCache &cache)
{
static GPUVertFormat format_data = single_attr_vertbuffer_format(
"selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
const int bezier_point_count = bezier_dst_offsets.total_size();
const int vert_count = curves.points_num() + bezier_point_count * 2;
GPU_vertbuf_init_with_format(cache.edit_points_selection, &format_data);
GPU_vertbuf_data_alloc(cache.edit_points_selection, curves.points_num());
GPU_vertbuf_data_alloc(cache.edit_points_selection, vert_count);
MutableSpan<float> data(static_cast<float *>(GPU_vertbuf_get_data(cache.edit_points_selection)),
curves.points_num());
vert_count);
const VArray<float> attribute = *curves.attributes().lookup_or_default<float>(
".selection", bke::AttrDomain::Point, true);
attribute.materialize(data);
".selection", bke::AttrDomain::Point, 1.0f);
attribute.materialize(data.slice(0, curves.points_num()));
if (!bezier_point_count) {
return;
}
const VArray<float> attribute_left = *curves.attributes().lookup_or_default<float>(
".selection_handle_left", bke::AttrDomain::Point, 0.0f);
const VArray<float> attribute_right = *curves.attributes().lookup_or_default<float>(
".selection_handle_right", bke::AttrDomain::Point, 0.0f);
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
IndexRange dst_range = IndexRange::from_begin_size(curves.points_num(), bezier_point_count);
array_utils::gather_group_to_group(
points_by_curve, bezier_dst_offsets, bezier_curves, attribute_left, data.slice(dst_range));
dst_range = dst_range.shift(bezier_point_count);
array_utils::gather_group_to_group(
points_by_curve, bezier_dst_offsets, bezier_curves, attribute_right, data.slice(dst_range));
}
static void curves_batch_cache_ensure_edit_lines(const bke::CurvesGeometry &curves,
CurvesBatchCache &cache)
static void curves_batch_cache_ensure_edit_handles(const bke::CurvesGeometry &curves,
const IndexMask bezier_curves,
const OffsetIndices<int> bezier_offsets,
const IndexMask nurbs_curves,
const OffsetIndices<int> nurbs_offsets,
CurvesBatchCache &cache)
{
const int vert_len = curves.points_num();
const int curve_len = curves.curves_num();
const int index_len = vert_len + curve_len;
GPUIndexBufBuilder elb;
const int bezier_point_count = bezier_offsets.total_size();
/* Left and right handle will be appended for each Bezier point. */
const int vert_len = curves.points_num() + 2 * bezier_point_count;
/* For each point has 2 lines from 2 point and one restart entry. */
const int index_len_for_bezier_handles = 6 * bezier_point_count;
const VArray<bool> cyclic = curves.cyclic();
/* All NURBS control points plus restart for every curve.
* Add space for possible cyclic curves.
* If one point curves or two point cyclic curves are present, not all builder's buffer space
* will be used. */
const int index_len_for_nurbs = nurbs_offsets.total_size() + nurbs_curves.size() +
array_utils::count_booleans(cyclic, nurbs_curves);
const int index_len = index_len_for_bezier_handles + index_len_for_nurbs;
/* Use two index buffer builders for the same underlying memory. */
GPUIndexBufBuilder elb, right_elb;
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len);
memcpy(&right_elb, &elb, sizeof(elb));
right_elb.index_len = 3 * bezier_point_count;
const OffsetIndices points_by_curve = curves.points_by_curve();
for (const int i : curves.curves_range()) {
const IndexRange points = points_by_curve[i];
for (const int i_point : points) {
GPU_indexbuf_add_generic_vert(&elb, i_point);
bezier_curves.foreach_index([&](const int64_t src_i, const int64_t dst_i) {
IndexRange bezier_points = points_by_curve[src_i];
const int index_shift = curves.points_num() - bezier_points.first() +
bezier_offsets[dst_i].first();
for (const int point : bezier_points) {
const int point_left_i = index_shift + point;
GPU_indexbuf_add_generic_vert(&elb, point_left_i);
GPU_indexbuf_add_generic_vert(&elb, point);
GPU_indexbuf_add_primitive_restart(&elb);
GPU_indexbuf_add_generic_vert(&right_elb, point_left_i + bezier_point_count);
GPU_indexbuf_add_generic_vert(&right_elb, point);
GPU_indexbuf_add_primitive_restart(&right_elb);
}
GPU_indexbuf_add_primitive_restart(&elb);
}
});
nurbs_curves.foreach_index([&](const int64_t src_i) {
IndexRange curve_points = points_by_curve[src_i];
if (curve_points.size() <= 1) {
return;
}
for (const int point : curve_points) {
GPU_indexbuf_add_generic_vert(&right_elb, point);
}
if (cyclic[src_i] && curve_points.size() > 2) {
GPU_indexbuf_add_generic_vert(&right_elb, curve_points.first());
}
GPU_indexbuf_add_primitive_restart(&right_elb);
});
GPU_indexbuf_join(&elb, &right_elb);
GPU_indexbuf_build_in_place(&elb, cache.edit_handles_ibo);
GPU_indexbuf_build_in_place(&elb, cache.edit_lines_ibo);
CurvesUboStorage ubo_storage{bezier_point_count};
GPU_uniformbuf_update(cache.curves_ubo_storage, &ubo_storage);
}
static void curves_batch_cache_ensure_procedural_final_attr(CurvesEvalCache &cache,
@ -628,7 +828,9 @@ void DRW_curves_batch_cache_validate(Curves *curves)
void DRW_curves_batch_cache_free(Curves *curves)
{
curves_batch_cache_clear(*curves);
MEM_delete(static_cast<CurvesBatchCache *>(curves->batch_cache));
CurvesBatchCache *batch_cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
DRW_UBO_FREE_SAFE(batch_cache->curves_ubo_storage);
MEM_delete(batch_cache);
curves->batch_cache = nullptr;
}
@ -665,16 +867,28 @@ int DRW_curves_material_count_get(const Curves *curves)
return max_ii(1, curves->totcol);
}
GPUUniformBuf *DRW_curves_batch_cache_ubo_storage(Curves *curves)
{
CurvesBatchCache &cache = curves_batch_cache_get(*curves);
return cache.curves_ubo_storage;
}
GPUBatch *DRW_curves_batch_cache_get_edit_points(Curves *curves)
{
CurvesBatchCache &cache = curves_batch_cache_get(*curves);
return DRW_batch_request(&cache.edit_points);
}
GPUBatch *DRW_curves_batch_cache_get_edit_lines(Curves *curves)
GPUBatch *DRW_curves_batch_cache_get_edit_curves_handles(Curves *curves)
{
CurvesBatchCache &cache = curves_batch_cache_get(*curves);
return DRW_batch_request(&cache.edit_lines);
return DRW_batch_request(&cache.edit_handles);
}
GPUBatch *DRW_curves_batch_cache_get_edit_curves_lines(Curves *curves)
{
CurvesBatchCache &cache = curves_batch_cache_get(*curves);
return DRW_batch_request(&cache.edit_curves_lines);
}
GPUVertBuf **DRW_curves_texture_for_evaluated_attribute(Curves *curves,
@ -713,6 +927,53 @@ GPUVertBuf **DRW_curves_texture_for_evaluated_attribute(Curves *curves,
}
}
static void curves_batch_cache_ensure_edit_curves_lines_ibo(const bke::CurvesGeometry &curves,
CurvesBatchCache &cache)
{
const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
int edges_len = 0;
for (const int i : curves.curves_range()) {
edges_len += bke::curves::segments_num(points_by_curve[i].size(), cyclic[i]);
}
const int index_len = edges_len + curves.curves_num() * 2;
GPUIndexBufBuilder elb;
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, points_by_curve.total_size());
for (const int i : curves.curves_range()) {
const IndexRange points = points_by_curve[i];
if (cyclic[i] && points.size() > 1) {
GPU_indexbuf_add_generic_vert(&elb, points.last());
}
for (const int i_point : points) {
GPU_indexbuf_add_generic_vert(&elb, i_point);
}
GPU_indexbuf_add_primitive_restart(&elb);
}
GPU_indexbuf_build_in_place(&elb, cache.edit_curves_lines_ibo);
}
static void curves_batch_cache_ensure_edit_curves_lines_pos(
const bke::CurvesGeometry &curves,
const bke::crazyspace::GeometryDeformation & /*deformation*/,
CurvesBatchCache &cache)
{
static uint attr_id;
static GPUVertFormat format = single_attr_vertbuffer_format(
"pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT, attr_id);
/* TODO: Deform curves using deformations. */
const Span<float3> positions = curves.evaluated_positions();
GPU_vertbuf_init_with_format(cache.edit_curves_lines_pos, &format);
GPU_vertbuf_data_alloc(cache.edit_curves_lines_pos, positions.size());
GPU_vertbuf_attr_fill(cache.edit_curves_lines_pos, attr_id, positions.data());
}
void DRW_curves_batch_cache_create_requested(Object *ob)
{
Curves *curves_id = static_cast<Curves *>(ob->data);
@ -725,6 +986,16 @@ void DRW_curves_batch_cache_create_requested(Object *ob)
draw::CurvesBatchCache &cache = draw::curves_batch_cache_get(*curves_id);
bke::CurvesGeometry &curves_orig = curves_orig_id->geometry.wrap();
IndexMaskMemory memory;
const IndexMask bezier_curves = bke::curves::indices_for_type(curves_orig.curve_types(),
curves_orig.curve_type_counts(),
CURVE_TYPE_BEZIER,
curves_orig.curves_range(),
memory);
Array<int> bezier_point_offset_data(bezier_curves.size() + 1);
const OffsetIndices<int> bezier_offsets = offset_indices::gather_selected_offsets(
curves_orig.points_by_curve(), bezier_curves, bezier_point_offset_data);
const bke::crazyspace::GeometryDeformation deformation =
bke::crazyspace::get_evaluated_curves_deformation(ob, *ob_orig);
@ -732,19 +1003,45 @@ void DRW_curves_batch_cache_create_requested(Object *ob)
DRW_vbo_request(cache.edit_points, &cache.edit_points_pos);
DRW_vbo_request(cache.edit_points, &cache.edit_points_selection);
}
if (DRW_batch_requested(cache.edit_lines, GPU_PRIM_LINE_STRIP)) {
DRW_ibo_request(cache.edit_lines, &cache.edit_lines_ibo);
DRW_vbo_request(cache.edit_lines, &cache.edit_points_pos);
DRW_vbo_request(cache.edit_lines, &cache.edit_points_selection);
if (DRW_batch_requested(cache.edit_handles, GPU_PRIM_LINE_STRIP)) {
DRW_ibo_request(cache.edit_handles, &cache.edit_handles_ibo);
DRW_vbo_request(cache.edit_handles, &cache.edit_points_pos);
DRW_vbo_request(cache.edit_handles, &cache.edit_points_data);
DRW_vbo_request(cache.edit_handles, &cache.edit_points_selection);
}
if (DRW_batch_requested(cache.edit_curves_lines, GPU_PRIM_LINE_STRIP)) {
DRW_vbo_request(cache.edit_curves_lines, &cache.edit_curves_lines_pos);
DRW_ibo_request(cache.edit_curves_lines, &cache.edit_curves_lines_ibo);
}
if (DRW_vbo_requested(cache.edit_points_pos)) {
curves_batch_cache_ensure_edit_points_pos(curves_orig, deformation.positions, cache);
curves_batch_cache_ensure_edit_points_pos_and_data(
curves_orig, bezier_curves, bezier_offsets, deformation, cache);
}
if (DRW_vbo_requested(cache.edit_points_selection)) {
curves_batch_cache_ensure_edit_points_selection(curves_orig, cache);
curves_batch_cache_ensure_edit_points_selection(
curves_orig, bezier_curves, bezier_offsets, cache);
}
if (DRW_ibo_requested(cache.edit_lines_ibo)) {
curves_batch_cache_ensure_edit_lines(curves_orig, cache);
if (DRW_ibo_requested(cache.edit_handles_ibo)) {
IndexMaskMemory nurbs_memory;
const IndexMask nurbs_curves = bke::curves::indices_for_type(curves_orig.curve_types(),
curves_orig.curve_type_counts(),
CURVE_TYPE_NURBS,
curves_orig.curves_range(),
nurbs_memory);
Array<int> nurbs_point_offset_data(nurbs_curves.size() + 1);
const OffsetIndices<int> nurbs_offsets = offset_indices::gather_selected_offsets(
curves_orig.points_by_curve(), nurbs_curves, nurbs_point_offset_data);
curves_batch_cache_ensure_edit_handles(
curves_orig, bezier_curves, bezier_offsets, nurbs_curves, nurbs_offsets, cache);
}
if (DRW_vbo_requested(cache.edit_curves_lines_pos)) {
curves_batch_cache_ensure_edit_curves_lines_pos(curves_orig, deformation, cache);
}
if (DRW_ibo_requested(cache.edit_curves_lines_ibo)) {
curves_batch_cache_ensure_edit_curves_lines_ibo(curves_orig, cache);
}
}