This unifies vertex and texture data formats into a single base enum class. `TextureFormat` and `VertexFormat` then mask the invalid format for their respective usage. Having a base enum allows casting between `TextureFormat` and `VertexFormat` possible (needed for Buffer Textures). It also makes it easier to write and read data to buffers/textures as each format will have an associated host type. These enum is generated from MACRO expansion. This allow to centralize all information about the formats in one place. This avoid duplicating the list of enums for each backend. This only creates the new enum. Porting older enums will be done in other PRs. Normalized integer CPU format are missing and waiting for #130640 Rel #130632 Pull Request: https://projects.blender.org/blender/blender/pulls/138069
1199 lines
45 KiB
C++
1199 lines
45 KiB
C++
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup draw
|
|
*
|
|
* \brief Curves API for render engines
|
|
*/
|
|
|
|
#include <cstring>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_array_utils.hh"
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math_base.h"
|
|
#include "BLI_math_vector.hh"
|
|
#include "BLI_math_vector_types.hh"
|
|
#include "BLI_span.hh"
|
|
#include "BLI_string.h"
|
|
#include "BLI_task.hh"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_curves_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
|
|
#include "DEG_depsgraph_query.hh"
|
|
|
|
#include "BKE_crazyspace.hh"
|
|
#include "BKE_curves.hh"
|
|
#include "BKE_curves_utils.hh"
|
|
#include "BKE_customdata.hh"
|
|
#include "BKE_geometry_set.hh"
|
|
|
|
#include "GPU_batch.hh"
|
|
#include "GPU_context.hh"
|
|
#include "GPU_material.hh"
|
|
#include "GPU_texture.hh"
|
|
|
|
#include "DRW_render.hh"
|
|
|
|
#include "draw_attributes.hh"
|
|
#include "draw_cache_impl.hh" /* own include */
|
|
#include "draw_cache_inline.hh"
|
|
#include "draw_curves_private.hh" /* own include */
|
|
|
|
namespace blender::draw {
|
|
|
|
#define EDIT_CURVES_NURBS_CONTROL_POINT (1u)
|
|
#define EDIT_CURVES_BEZIER_HANDLE (1u << 1)
|
|
#define EDIT_CURVES_ACTIVE_HANDLE (1u << 2)
|
|
/* Bezier curve control point lying on the curve.
|
|
* The one between left and right handles. */
|
|
#define EDIT_CURVES_BEZIER_KNOT (1u << 3)
|
|
#define EDIT_CURVES_HANDLE_TYPES_SHIFT (4u)
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
struct CurvesBatchCache {
|
|
CurvesEvalCache eval_cache;
|
|
|
|
gpu::Batch *edit_points;
|
|
gpu::Batch *edit_handles;
|
|
|
|
gpu::Batch *sculpt_cage;
|
|
gpu::IndexBuf *sculpt_cage_ibo;
|
|
|
|
/* Crazy-space point positions for original points. */
|
|
gpu::VertBuf *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.
|
|
*/
|
|
gpu::VertBuf *edit_points_data;
|
|
|
|
/* Selection of original points. */
|
|
gpu::VertBuf *edit_points_selection;
|
|
|
|
gpu::IndexBuf *edit_handles_ibo;
|
|
|
|
gpu::Batch *edit_curves_lines;
|
|
gpu::VertBuf *edit_curves_lines_pos;
|
|
gpu::IndexBuf *edit_curves_lines_ibo;
|
|
|
|
/* Whether the cache is invalid. */
|
|
bool is_dirty;
|
|
|
|
/**
|
|
* The draw cache extraction is currently not multi-threaded for multiple objects, but if it was,
|
|
* some locking would be necessary because multiple objects can use the same curves data with
|
|
* different materials, etc. This is a placeholder to make multi-threading easier in the future.
|
|
*/
|
|
Mutex render_mutex;
|
|
};
|
|
|
|
static bool batch_cache_is_dirty(const Curves &curves)
|
|
{
|
|
const CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache);
|
|
return (cache && cache->is_dirty == false);
|
|
}
|
|
|
|
static void init_batch_cache(Curves &curves)
|
|
{
|
|
CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache);
|
|
|
|
if (!cache) {
|
|
cache = MEM_new<CurvesBatchCache>(__func__);
|
|
curves.batch_cache = cache;
|
|
}
|
|
else {
|
|
cache->eval_cache = {};
|
|
}
|
|
|
|
cache->is_dirty = false;
|
|
}
|
|
|
|
static void discard_attributes(CurvesEvalCache &eval_cache)
|
|
{
|
|
for (const int i : IndexRange(GPU_MAX_ATTR)) {
|
|
GPU_VERTBUF_DISCARD_SAFE(eval_cache.proc_attributes_buf[i]);
|
|
}
|
|
|
|
for (const int j : IndexRange(GPU_MAX_ATTR)) {
|
|
GPU_VERTBUF_DISCARD_SAFE(eval_cache.final.attributes_buf[j]);
|
|
}
|
|
|
|
drw_attributes_clear(&eval_cache.final.attr_used);
|
|
}
|
|
|
|
static void 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_handles_ibo);
|
|
|
|
GPU_BATCH_DISCARD_SAFE(cache->edit_points);
|
|
GPU_BATCH_DISCARD_SAFE(cache->edit_handles);
|
|
|
|
GPU_INDEXBUF_DISCARD_SAFE(cache->sculpt_cage_ibo);
|
|
GPU_BATCH_DISCARD_SAFE(cache->sculpt_cage);
|
|
|
|
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 clear_final_data(CurvesEvalFinalCache &final_cache)
|
|
{
|
|
GPU_VERTBUF_DISCARD_SAFE(final_cache.proc_buf);
|
|
GPU_BATCH_DISCARD_SAFE(final_cache.proc_hairs);
|
|
for (const int j : IndexRange(GPU_MAX_ATTR)) {
|
|
GPU_VERTBUF_DISCARD_SAFE(final_cache.attributes_buf[j]);
|
|
}
|
|
}
|
|
|
|
static void clear_eval_data(CurvesEvalCache &eval_cache)
|
|
{
|
|
/* TODO: more granular update tagging. */
|
|
GPU_VERTBUF_DISCARD_SAFE(eval_cache.proc_point_buf);
|
|
GPU_VERTBUF_DISCARD_SAFE(eval_cache.proc_length_buf);
|
|
GPU_VERTBUF_DISCARD_SAFE(eval_cache.proc_strand_buf);
|
|
GPU_VERTBUF_DISCARD_SAFE(eval_cache.proc_strand_seg_buf);
|
|
|
|
clear_final_data(eval_cache.final);
|
|
|
|
discard_attributes(eval_cache);
|
|
}
|
|
|
|
static void clear_batch_cache(Curves &curves)
|
|
{
|
|
CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache);
|
|
if (!cache) {
|
|
return;
|
|
}
|
|
|
|
clear_eval_data(cache->eval_cache);
|
|
clear_edit_data(cache);
|
|
}
|
|
|
|
static CurvesBatchCache &get_batch_cache(Curves &curves)
|
|
{
|
|
DRW_curves_batch_cache_validate(&curves);
|
|
return *static_cast<CurvesBatchCache *>(curves.batch_cache);
|
|
}
|
|
|
|
struct PositionAndParameter {
|
|
float3 position;
|
|
float parameter;
|
|
};
|
|
|
|
static void fill_points_position_time_vbo(const OffsetIndices<int> points_by_curve,
|
|
const Span<float3> positions,
|
|
MutableSpan<PositionAndParameter> posTime_data,
|
|
MutableSpan<float> hairLength_data)
|
|
{
|
|
threading::parallel_for(points_by_curve.index_range(), 1024, [&](const IndexRange range) {
|
|
for (const int i_curve : range) {
|
|
const IndexRange points = points_by_curve[i_curve];
|
|
|
|
Span<float3> curve_positions = positions.slice(points);
|
|
MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points);
|
|
|
|
float total_len = 0.0f;
|
|
for (const int i_point : curve_positions.index_range()) {
|
|
if (i_point > 0) {
|
|
total_len += math::distance(curve_positions[i_point - 1], curve_positions[i_point]);
|
|
}
|
|
curve_posTime_data[i_point].position = curve_positions[i_point];
|
|
curve_posTime_data[i_point].parameter = total_len;
|
|
}
|
|
hairLength_data[i_curve] = total_len;
|
|
|
|
/* Assign length value. */
|
|
if (total_len > 0.0f) {
|
|
const float factor = 1.0f / total_len;
|
|
/* Divide by total length to have a [0-1] number. */
|
|
for (const int i_point : curve_positions.index_range()) {
|
|
curve_posTime_data[i_point].parameter *= factor;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
static void create_points_position_time_vbo(const bke::CurvesGeometry &curves,
|
|
CurvesEvalCache &cache)
|
|
{
|
|
GPUVertFormat format = {0};
|
|
GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
|
|
|
cache.proc_point_buf = GPU_vertbuf_create_with_format_ex(
|
|
format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
|
|
GPU_vertbuf_data_alloc(*cache.proc_point_buf, cache.points_num);
|
|
|
|
GPUVertFormat length_format = {0};
|
|
GPU_vertformat_attr_add(&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
|
|
|
|
cache.proc_length_buf = GPU_vertbuf_create_with_format_ex(
|
|
length_format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
|
|
GPU_vertbuf_data_alloc(*cache.proc_length_buf, cache.curves_num);
|
|
|
|
/* TODO: Only create hairLength VBO when necessary. */
|
|
fill_points_position_time_vbo(curves.points_by_curve(),
|
|
curves.positions(),
|
|
cache.proc_point_buf->data<PositionAndParameter>(),
|
|
cache.proc_length_buf->data<float>());
|
|
}
|
|
|
|
static uint32_t bezier_data_value(int8_t handle_type, bool is_active)
|
|
{
|
|
return (handle_type << EDIT_CURVES_HANDLE_TYPES_SHIFT) | EDIT_CURVES_BEZIER_HANDLE |
|
|
(is_active ? EDIT_CURVES_ACTIVE_HANDLE : 0);
|
|
}
|
|
|
|
static int handles_and_points_num(const int points_num, const OffsetIndices<int> bezier_offsets)
|
|
{
|
|
return points_num + bezier_offsets.total_size() * 2;
|
|
}
|
|
|
|
static IndexRange handle_range_left(const int points_num, const OffsetIndices<int> bezier_offsets)
|
|
{
|
|
return IndexRange(points_num, bezier_offsets.total_size());
|
|
}
|
|
|
|
static IndexRange handle_range_right(const int points_num, const OffsetIndices<int> bezier_offsets)
|
|
{
|
|
return IndexRange(points_num + bezier_offsets.total_size(), bezier_offsets.total_size());
|
|
}
|
|
|
|
static void extract_edit_data(const OffsetIndices<int> points_by_curve,
|
|
const IndexMask &curve_selection,
|
|
const VArray<bool> &selection_attr,
|
|
const bool mark_active,
|
|
const uint32_t fill_value,
|
|
MutableSpan<uint32_t> data)
|
|
{
|
|
curve_selection.foreach_index(GrainSize(256), [&](const int curve) {
|
|
const IndexRange points = points_by_curve[curve];
|
|
bool is_active = false;
|
|
if (mark_active) {
|
|
is_active = array_utils::count_booleans(selection_attr, points) > 0;
|
|
}
|
|
uint32_t data_value = fill_value | (is_active ? EDIT_CURVES_ACTIVE_HANDLE : 0u);
|
|
data.slice(points).fill(data_value);
|
|
});
|
|
}
|
|
|
|
static void create_edit_points_data(const OffsetIndices<int> points_by_curve,
|
|
const IndexMask &catmull_rom_curves,
|
|
const IndexMask &poly_curves,
|
|
const IndexMask &bezier_curves,
|
|
const IndexMask &nurbs_curves,
|
|
const OffsetIndices<int> bezier_offsets,
|
|
const bke::CurvesGeometry &curves,
|
|
gpu::VertBuf &vbo)
|
|
{
|
|
const int points_num = points_by_curve.total_size();
|
|
const bke::AttributeAccessor attributes = curves.attributes();
|
|
const VArray selection = *attributes.lookup_or_default<bool>(
|
|
".selection", bke::AttrDomain::Point, true);
|
|
|
|
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
|
"data", GPU_COMP_U32, 1, GPU_FETCH_INT);
|
|
GPU_vertbuf_init_with_format(vbo, format);
|
|
GPU_vertbuf_data_alloc(vbo, handles_and_points_num(points_num, bezier_offsets));
|
|
MutableSpan<uint32_t> data = vbo.data<uint32_t>();
|
|
|
|
extract_edit_data(points_by_curve, catmull_rom_curves, selection, false, 0, data);
|
|
|
|
extract_edit_data(points_by_curve, poly_curves, selection, false, 0, data);
|
|
|
|
if (!bezier_curves.is_empty()) {
|
|
const VArray<int8_t> type_right = curves.handle_types_left();
|
|
const VArray<int8_t> types_left = curves.handle_types_right();
|
|
const VArray selection_left = *attributes.lookup_or_default<bool>(
|
|
".selection_handle_left", bke::AttrDomain::Point, true);
|
|
const VArray selection_right = *attributes.lookup_or_default<bool>(
|
|
".selection_handle_right", bke::AttrDomain::Point, true);
|
|
|
|
MutableSpan data_left = data.slice(handle_range_left(points_num, bezier_offsets));
|
|
MutableSpan data_right = data.slice(handle_range_right(points_num, bezier_offsets));
|
|
|
|
bezier_curves.foreach_index(GrainSize(256), [&](const int curve, const int64_t pos) {
|
|
const IndexRange points = points_by_curve[curve];
|
|
const IndexRange bezier_range = bezier_offsets[pos];
|
|
for (const int i : points.index_range()) {
|
|
const int point = points[i];
|
|
data[point] = EDIT_CURVES_BEZIER_KNOT;
|
|
|
|
const bool selected = selection[point] || selection_left[point] || selection_right[point];
|
|
const int bezier_point = bezier_range[i];
|
|
data_left[bezier_point] = bezier_data_value(type_right[point], selected);
|
|
data_right[bezier_point] = bezier_data_value(types_left[point], selected);
|
|
}
|
|
});
|
|
}
|
|
|
|
extract_edit_data(
|
|
points_by_curve, nurbs_curves, selection, true, EDIT_CURVES_NURBS_CONTROL_POINT, data);
|
|
}
|
|
|
|
static void create_edit_points_position(const bke::CurvesGeometry &curves,
|
|
const OffsetIndices<int> points_by_curve,
|
|
const IndexMask &bezier_curves,
|
|
const OffsetIndices<int> bezier_offsets,
|
|
const bke::crazyspace::GeometryDeformation deformation,
|
|
gpu::VertBuf &vbo)
|
|
{
|
|
const Span<float3> positions = deformation.positions;
|
|
const int points_num = positions.size();
|
|
|
|
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
|
"pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
|
|
GPU_vertbuf_init_with_format(vbo, format);
|
|
GPU_vertbuf_data_alloc(vbo, handles_and_points_num(points_num, bezier_offsets));
|
|
|
|
MutableSpan<float3> data = vbo.data<float3>();
|
|
data.take_front(positions.size()).copy_from(positions);
|
|
|
|
/* TODO: Use deformed left_handle_positions and left_handle_positions. */
|
|
array_utils::gather_group_to_group(points_by_curve,
|
|
bezier_offsets,
|
|
bezier_curves,
|
|
curves.handle_positions_left(),
|
|
data.slice(handle_range_left(points_num, bezier_offsets)));
|
|
array_utils::gather_group_to_group(points_by_curve,
|
|
bezier_offsets,
|
|
bezier_curves,
|
|
curves.handle_positions_right(),
|
|
data.slice(handle_range_right(points_num, bezier_offsets)));
|
|
}
|
|
|
|
static void create_edit_points_selection(const OffsetIndices<int> points_by_curve,
|
|
const IndexMask &bezier_curves,
|
|
const OffsetIndices<int> bezier_offsets,
|
|
const bke::AttributeAccessor attributes,
|
|
gpu::VertBuf &vbo)
|
|
{
|
|
static const GPUVertFormat format_data = GPU_vertformat_from_attribute(
|
|
"selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
|
|
|
|
const int points_num = points_by_curve.total_size();
|
|
GPU_vertbuf_init_with_format(vbo, format_data);
|
|
GPU_vertbuf_data_alloc(vbo, handles_and_points_num(points_num, bezier_offsets));
|
|
MutableSpan<float> data = vbo.data<float>();
|
|
|
|
const VArray attribute = *attributes.lookup_or_default<float>(
|
|
".selection", bke::AttrDomain::Point, 1.0f);
|
|
attribute.materialize(data.take_front(points_num));
|
|
|
|
if (bezier_curves.is_empty()) {
|
|
return;
|
|
}
|
|
|
|
const VArray selection_left = *attributes.lookup_or_default<float>(
|
|
".selection_handle_left", bke::AttrDomain::Point, 1.0f);
|
|
const VArray selection_right = *attributes.lookup_or_default<float>(
|
|
".selection_handle_right", bke::AttrDomain::Point, 1.0f);
|
|
|
|
array_utils::gather_group_to_group(points_by_curve,
|
|
bezier_offsets,
|
|
bezier_curves,
|
|
selection_left,
|
|
data.slice(handle_range_left(points_num, bezier_offsets)));
|
|
array_utils::gather_group_to_group(points_by_curve,
|
|
bezier_offsets,
|
|
bezier_curves,
|
|
selection_right,
|
|
data.slice(handle_range_right(points_num, bezier_offsets)));
|
|
}
|
|
|
|
static void create_lines_ibo_no_cyclic(const OffsetIndices<int> points_by_curve,
|
|
gpu::IndexBuf &ibo)
|
|
{
|
|
const int points_num = points_by_curve.total_size();
|
|
const int curves_num = points_by_curve.size();
|
|
const int indices_num = points_num + curves_num;
|
|
GPUIndexBufBuilder builder;
|
|
GPU_indexbuf_init(&builder, GPU_PRIM_LINE_STRIP, indices_num, points_num);
|
|
MutableSpan<uint> ibo_data = GPU_indexbuf_get_data(&builder);
|
|
threading::parallel_for(IndexRange(curves_num), 1024, [&](const IndexRange range) {
|
|
for (const int curve : range) {
|
|
const IndexRange points = points_by_curve[curve];
|
|
const IndexRange ibo_range = IndexRange(points.start() + curve, points.size() + 1);
|
|
for (const int i : points.index_range()) {
|
|
ibo_data[ibo_range[i]] = points[i];
|
|
}
|
|
ibo_data[ibo_range.last()] = gpu::RESTART_INDEX;
|
|
}
|
|
});
|
|
GPU_indexbuf_build_in_place_ex(&builder, 0, points_num, true, &ibo);
|
|
}
|
|
|
|
static void create_lines_ibo_with_cyclic(const OffsetIndices<int> points_by_curve,
|
|
const Span<bool> cyclic,
|
|
gpu::IndexBuf &ibo)
|
|
{
|
|
const int points_num = points_by_curve.total_size();
|
|
const int curves_num = points_by_curve.size();
|
|
const int indices_num = points_num + curves_num * 2;
|
|
GPUIndexBufBuilder builder;
|
|
GPU_indexbuf_init(&builder, GPU_PRIM_LINE_STRIP, indices_num, points_num);
|
|
MutableSpan<uint> ibo_data = GPU_indexbuf_get_data(&builder);
|
|
threading::parallel_for(IndexRange(curves_num), 1024, [&](const IndexRange range) {
|
|
for (const int curve : range) {
|
|
const IndexRange points = points_by_curve[curve];
|
|
const IndexRange ibo_range = IndexRange(points.start() + curve * 2, points.size() + 2);
|
|
for (const int i : points.index_range()) {
|
|
ibo_data[ibo_range[i]] = points[i];
|
|
}
|
|
ibo_data[ibo_range.last(1)] = cyclic[curve] ? points.first() : gpu::RESTART_INDEX;
|
|
ibo_data[ibo_range.last()] = gpu::RESTART_INDEX;
|
|
}
|
|
});
|
|
GPU_indexbuf_build_in_place_ex(&builder, 0, points_num, true, &ibo);
|
|
}
|
|
|
|
static void create_lines_ibo_with_cyclic(const OffsetIndices<int> points_by_curve,
|
|
const VArray<bool> &cyclic,
|
|
gpu::IndexBuf &ibo)
|
|
{
|
|
const array_utils::BooleanMix cyclic_mix = array_utils::booleans_mix_calc(cyclic);
|
|
if (cyclic_mix == array_utils::BooleanMix::AllFalse) {
|
|
create_lines_ibo_no_cyclic(points_by_curve, ibo);
|
|
}
|
|
else {
|
|
const VArraySpan<bool> cyclic_span(cyclic);
|
|
create_lines_ibo_with_cyclic(points_by_curve, cyclic_span, ibo);
|
|
}
|
|
}
|
|
|
|
static void extract_curve_lines(const OffsetIndices<int> points_by_curve,
|
|
const VArray<bool> &cyclic,
|
|
const IndexMask &selection,
|
|
const int cyclic_segment_offset,
|
|
MutableSpan<uint2> lines)
|
|
{
|
|
selection.foreach_index(GrainSize(512), [&](const int curve) {
|
|
const IndexRange points = points_by_curve[curve];
|
|
MutableSpan<uint2> curve_lines = lines.slice(points.start() + cyclic_segment_offset,
|
|
points.size() + 1);
|
|
for (const int i : IndexRange(points.size() - 1)) {
|
|
const int point = points[i];
|
|
curve_lines[i] = uint2(point, point + 1);
|
|
}
|
|
if (cyclic[curve]) {
|
|
curve_lines.last() = uint2(points.first(), points.last());
|
|
}
|
|
else {
|
|
curve_lines.last() = uint2(points.last(), points.last());
|
|
}
|
|
});
|
|
}
|
|
|
|
static void calc_edit_handles_ibo(const OffsetIndices<int> points_by_curve,
|
|
const IndexMask &catmull_rom_curves,
|
|
const IndexMask &poly_curves,
|
|
const IndexMask &bezier_curves,
|
|
const IndexMask &nurbs_curves,
|
|
const OffsetIndices<int> bezier_offsets,
|
|
const VArray<bool> &cyclic,
|
|
gpu::IndexBuf &ibo)
|
|
{
|
|
const int curves_num = points_by_curve.size();
|
|
const int points_num = points_by_curve.total_size();
|
|
const int non_bezier_points_num = points_num - bezier_offsets.total_size();
|
|
const int non_bezier_curves_num = curves_num - bezier_curves.size();
|
|
|
|
int lines_num = 0;
|
|
/* Lines for all non-cyclic non-Bezier segments. */
|
|
lines_num += non_bezier_points_num;
|
|
/* Lines for all potential non-Bezier cyclic segments. */
|
|
lines_num += non_bezier_curves_num;
|
|
/* Lines for all Bezier handles. */
|
|
lines_num += bezier_offsets.total_size() * 2;
|
|
|
|
GPUIndexBufBuilder builder;
|
|
GPU_indexbuf_init(
|
|
&builder, GPU_PRIM_LINES, lines_num, handles_and_points_num(points_num, bezier_offsets));
|
|
MutableSpan<uint2> lines = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
|
|
|
int cyclic_segment_offset = 0;
|
|
extract_curve_lines(points_by_curve, cyclic, catmull_rom_curves, cyclic_segment_offset, lines);
|
|
cyclic_segment_offset += catmull_rom_curves.size();
|
|
|
|
extract_curve_lines(points_by_curve, cyclic, poly_curves, cyclic_segment_offset, lines);
|
|
cyclic_segment_offset += catmull_rom_curves.size();
|
|
|
|
if (!bezier_curves.is_empty()) {
|
|
const IndexRange handles_left = handle_range_left(points_num, bezier_offsets);
|
|
const IndexRange handles_right = handle_range_right(points_num, bezier_offsets);
|
|
|
|
MutableSpan lines_left = lines.slice(
|
|
handle_range_left(non_bezier_points_num, bezier_offsets).shift(non_bezier_curves_num));
|
|
MutableSpan lines_right = lines.slice(
|
|
handle_range_right(non_bezier_points_num, bezier_offsets).shift(non_bezier_curves_num));
|
|
|
|
bezier_curves.foreach_index(GrainSize(512), [&](const int curve, const int pos) {
|
|
const IndexRange points = points_by_curve[curve];
|
|
const IndexRange bezier_point_range = bezier_offsets[pos];
|
|
for (const int i : points.index_range()) {
|
|
const int point = points[i];
|
|
const int bezier_point = bezier_point_range[i];
|
|
lines_left[bezier_point] = uint2(handles_left[bezier_point], point);
|
|
lines_right[bezier_point] = uint2(handles_right[bezier_point], point);
|
|
}
|
|
});
|
|
}
|
|
|
|
extract_curve_lines(points_by_curve, cyclic, nurbs_curves, cyclic_segment_offset, lines);
|
|
|
|
GPU_indexbuf_build_in_place_ex(
|
|
&builder, 0, handles_and_points_num(points_num, bezier_offsets), false, &ibo);
|
|
}
|
|
|
|
static void alloc_final_attribute_vbo(CurvesEvalCache &cache,
|
|
const GPUVertFormat &format,
|
|
const int index,
|
|
const char * /*name*/)
|
|
{
|
|
cache.final.attributes_buf[index] = GPU_vertbuf_create_with_format_ex(
|
|
format, GPU_USAGE_DEVICE_ONLY | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
|
|
|
|
/* Create a destination buffer for the transform feedback. Sized appropriately */
|
|
/* Those are points! not line segments. */
|
|
GPU_vertbuf_data_alloc(*cache.final.attributes_buf[index],
|
|
cache.final.resolution * cache.curves_num);
|
|
}
|
|
|
|
static gpu::VertBufPtr ensure_control_point_attribute(const Curves &curves_id,
|
|
const StringRef name,
|
|
const GPUVertFormat &format,
|
|
bool &r_is_point_domain)
|
|
{
|
|
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format_ex(
|
|
format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY));
|
|
|
|
const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
|
|
const bke::AttributeAccessor attributes = curves.wrap().attributes();
|
|
|
|
/* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done
|
|
* by OpenGL to float4 for a scalar `s` will produce a `float4(s, 0, 0, 1)`. However, following
|
|
* the Blender convention, it should be `float4(s, s, s, 1)`. This could be resolved using a
|
|
* similar texture state swizzle to map the attribute correctly as for volume attributes, so we
|
|
* can control the conversion ourselves. */
|
|
const bke::AttributeReader<ColorGeometry4f> attribute = attributes.lookup<ColorGeometry4f>(name);
|
|
if (!attribute) {
|
|
GPU_vertbuf_data_alloc(*vbo, curves.curves_num());
|
|
vbo->data<ColorGeometry4f>().fill({0.0f, 0.0f, 0.0f, 1.0f});
|
|
r_is_point_domain = false;
|
|
return vbo;
|
|
}
|
|
|
|
r_is_point_domain = attribute.domain == bke::AttrDomain::Point;
|
|
GPU_vertbuf_data_alloc(*vbo, r_is_point_domain ? curves.points_num() : curves.curves_num());
|
|
attribute.varray.materialize(vbo->data<ColorGeometry4f>());
|
|
return vbo;
|
|
}
|
|
|
|
static void ensure_final_attribute(const Curves &curves,
|
|
const StringRef name,
|
|
const int index,
|
|
CurvesEvalCache &cache)
|
|
{
|
|
char sampler_name[32];
|
|
drw_curves_get_attribute_sampler_name(name, sampler_name);
|
|
|
|
GPUVertFormat format = {0};
|
|
/* All attributes use float4, see comment below. */
|
|
GPU_vertformat_attr_add(&format, sampler_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
|
|
|
if (!cache.proc_attributes_buf[index]) {
|
|
gpu::VertBufPtr vbo = ensure_control_point_attribute(
|
|
curves, name, format, cache.proc_attributes_point_domain[index]);
|
|
cache.proc_attributes_buf[index] = vbo.release();
|
|
}
|
|
|
|
/* Existing final data may have been for a different attribute (with a different name or domain),
|
|
* free the data. */
|
|
GPU_VERTBUF_DISCARD_SAFE(cache.final.attributes_buf[index]);
|
|
|
|
/* Ensure final data for points. */
|
|
if (cache.proc_attributes_point_domain[index]) {
|
|
alloc_final_attribute_vbo(cache, format, index, sampler_name);
|
|
}
|
|
}
|
|
|
|
static void fill_curve_offsets_vbos(const OffsetIndices<int> points_by_curve,
|
|
GPUVertBufRaw &data_step,
|
|
GPUVertBufRaw &seg_step)
|
|
{
|
|
for (const int i : points_by_curve.index_range()) {
|
|
const IndexRange points = points_by_curve[i];
|
|
|
|
*(uint *)GPU_vertbuf_raw_step(&data_step) = points.start();
|
|
*(uint *)GPU_vertbuf_raw_step(&seg_step) = points.size() - 1;
|
|
}
|
|
}
|
|
|
|
static void create_curve_offsets_vbos(const OffsetIndices<int> points_by_curve,
|
|
CurvesEvalCache &cache)
|
|
{
|
|
GPUVertBufRaw data_step, seg_step;
|
|
|
|
GPUVertFormat format_data = {0};
|
|
uint data_id = GPU_vertformat_attr_add(&format_data, "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
|
|
|
|
GPUVertFormat format_seg = {0};
|
|
uint seg_id = GPU_vertformat_attr_add(&format_seg, "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
|
|
|
|
/* Curve Data. */
|
|
cache.proc_strand_buf = GPU_vertbuf_create_with_format_ex(
|
|
format_data, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
|
|
GPU_vertbuf_data_alloc(*cache.proc_strand_buf, cache.curves_num);
|
|
GPU_vertbuf_attr_get_raw_data(cache.proc_strand_buf, data_id, &data_step);
|
|
|
|
cache.proc_strand_seg_buf = GPU_vertbuf_create_with_format_ex(
|
|
format_seg, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
|
|
GPU_vertbuf_data_alloc(*cache.proc_strand_seg_buf, cache.curves_num);
|
|
GPU_vertbuf_attr_get_raw_data(cache.proc_strand_seg_buf, seg_id, &seg_step);
|
|
|
|
fill_curve_offsets_vbos(points_by_curve, data_step, seg_step);
|
|
}
|
|
|
|
static void alloc_final_points_vbo(CurvesEvalCache &cache)
|
|
{
|
|
/* Same format as proc_point_buf. */
|
|
GPUVertFormat format = {0};
|
|
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
|
|
|
|
cache.final.proc_buf = GPU_vertbuf_create_with_format_ex(
|
|
format, GPU_USAGE_DEVICE_ONLY | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
|
|
|
|
/* Create a destination buffer for the transform feedback. Sized appropriately */
|
|
|
|
/* Those are points! not line segments. */
|
|
uint point_len = cache.final.resolution * cache.curves_num;
|
|
/* Avoid creating null sized VBO which can lead to crashes on certain platforms. */
|
|
point_len = max_ii(1, point_len);
|
|
|
|
GPU_vertbuf_data_alloc(*cache.final.proc_buf, point_len);
|
|
}
|
|
|
|
static void calc_final_indices(const bke::CurvesGeometry &curves,
|
|
CurvesEvalCache &cache,
|
|
const int thickness_res)
|
|
{
|
|
BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */
|
|
/* Determine prim type and element count.
|
|
* NOTE: Metal backend uses non-restart prim types for optimal HW performance. */
|
|
bool use_strip_prims = (GPU_backend_get_type() != GPU_BACKEND_METAL);
|
|
int verts_per_curve;
|
|
GPUPrimType prim_type;
|
|
|
|
if (use_strip_prims) {
|
|
/* +1 for primitive restart */
|
|
verts_per_curve = cache.final.resolution * thickness_res;
|
|
prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
|
|
}
|
|
else {
|
|
/* Use full primitive type. */
|
|
prim_type = (thickness_res == 1) ? GPU_PRIM_LINES : GPU_PRIM_TRIS;
|
|
int verts_per_segment = ((prim_type == GPU_PRIM_LINES) ? 2 : 6);
|
|
verts_per_curve = (cache.final.resolution - 1) * verts_per_segment;
|
|
}
|
|
|
|
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
|
"dummy", GPU_COMP_U32, 1, GPU_FETCH_INT);
|
|
|
|
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
|
GPU_vertbuf_data_alloc(*vbo, 1);
|
|
|
|
gpu::IndexBuf *ibo = nullptr;
|
|
eGPUBatchFlag owns_flag = GPU_BATCH_OWNS_VBO;
|
|
if (curves.curves_num()) {
|
|
ibo = GPU_indexbuf_build_curves_on_device(prim_type, curves.curves_num(), verts_per_curve);
|
|
owns_flag |= GPU_BATCH_OWNS_INDEX;
|
|
}
|
|
cache.final.proc_hairs = GPU_batch_create_ex(prim_type, vbo, ibo, owns_flag);
|
|
}
|
|
|
|
static bool ensure_attributes(const Curves &curves,
|
|
CurvesBatchCache &cache,
|
|
const GPUMaterial *gpu_material)
|
|
{
|
|
const CustomData &cd_curve = curves.geometry.curve_data;
|
|
const CustomData &cd_point = curves.geometry.point_data;
|
|
CurvesEvalFinalCache &final_cache = cache.eval_cache.final;
|
|
|
|
if (gpu_material) {
|
|
/* The following code should be kept in sync with `mesh_cd_calc_used_gpu_layers`. */
|
|
DRW_Attributes attrs_needed;
|
|
drw_attributes_clear(&attrs_needed);
|
|
ListBase gpu_attrs = GPU_material_attributes(gpu_material);
|
|
LISTBASE_FOREACH (const GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
|
|
const char *name = gpu_attr->name;
|
|
eCustomDataType type = static_cast<eCustomDataType>(gpu_attr->type);
|
|
int layer = -1;
|
|
std::optional<bke::AttrDomain> domain;
|
|
|
|
if (gpu_attr->type == CD_AUTO_FROM_NAME) {
|
|
/* We need to deduce what exact layer is used.
|
|
*
|
|
* We do it based on the specified name.
|
|
*/
|
|
if (name[0] != '\0') {
|
|
layer = CustomData_get_named_layer(&cd_curve, CD_PROP_FLOAT2, name);
|
|
type = CD_MTFACE;
|
|
domain = bke::AttrDomain::Curve;
|
|
|
|
if (layer == -1) {
|
|
/* Try to match a generic attribute, we use the first attribute domain with a
|
|
* matching name. */
|
|
if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) {
|
|
domain = bke::AttrDomain::Point;
|
|
}
|
|
else if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) {
|
|
domain = bke::AttrDomain::Curve;
|
|
}
|
|
else {
|
|
domain.reset();
|
|
layer = -1;
|
|
}
|
|
}
|
|
|
|
if (layer == -1) {
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
/* Fall back to the UV layer, which matches old behavior. */
|
|
type = CD_MTFACE;
|
|
}
|
|
}
|
|
else {
|
|
if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) {
|
|
domain = bke::AttrDomain::Curve;
|
|
}
|
|
else if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) {
|
|
domain = bke::AttrDomain::Point;
|
|
}
|
|
}
|
|
|
|
switch (type) {
|
|
case CD_MTFACE: {
|
|
if (layer == -1) {
|
|
layer = (name[0] != '\0') ?
|
|
CustomData_get_named_layer(&cd_curve, CD_PROP_FLOAT2, name) :
|
|
CustomData_get_render_layer(&cd_curve, CD_PROP_FLOAT2);
|
|
if (layer != -1) {
|
|
domain = bke::AttrDomain::Curve;
|
|
}
|
|
}
|
|
if (layer == -1) {
|
|
layer = (name[0] != '\0') ?
|
|
CustomData_get_named_layer(&cd_point, CD_PROP_FLOAT2, name) :
|
|
CustomData_get_render_layer(&cd_point, CD_PROP_FLOAT2);
|
|
if (layer != -1) {
|
|
domain = bke::AttrDomain::Point;
|
|
}
|
|
}
|
|
|
|
if (layer != -1 && name[0] == '\0' && domain.has_value()) {
|
|
name = CustomData_get_layer_name(
|
|
domain == bke::AttrDomain::Curve ? &cd_curve : &cd_point, CD_PROP_FLOAT2, layer);
|
|
}
|
|
|
|
if (layer != -1 && domain.has_value()) {
|
|
drw_attributes_add_request(&attrs_needed, name);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CD_TANGENT:
|
|
case CD_ORCO:
|
|
break;
|
|
|
|
case CD_PROP_BYTE_COLOR:
|
|
case CD_PROP_COLOR:
|
|
case CD_PROP_QUATERNION:
|
|
case CD_PROP_FLOAT3:
|
|
case CD_PROP_BOOL:
|
|
case CD_PROP_INT8:
|
|
case CD_PROP_INT32:
|
|
case CD_PROP_INT16_2D:
|
|
case CD_PROP_INT32_2D:
|
|
case CD_PROP_FLOAT:
|
|
case CD_PROP_FLOAT2: {
|
|
if (layer != -1 && domain.has_value()) {
|
|
drw_attributes_add_request(&attrs_needed, name);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!drw_attributes_overlap(&final_cache.attr_used, &attrs_needed)) {
|
|
/* Some new attributes have been added, free all and start over. */
|
|
for (const int i : IndexRange(GPU_MAX_ATTR)) {
|
|
GPU_VERTBUF_DISCARD_SAFE(final_cache.attributes_buf[i]);
|
|
GPU_VERTBUF_DISCARD_SAFE(cache.eval_cache.proc_attributes_buf[i]);
|
|
}
|
|
drw_attributes_merge(&final_cache.attr_used, &attrs_needed, cache.render_mutex);
|
|
}
|
|
drw_attributes_merge(&final_cache.attr_used_over_time, &attrs_needed, cache.render_mutex);
|
|
}
|
|
|
|
bool need_tf_update = false;
|
|
|
|
for (const int i : IndexRange(final_cache.attr_used.num_requests)) {
|
|
const DRW_AttributeRequest &request = final_cache.attr_used.requests[i];
|
|
|
|
if (cache.eval_cache.final.attributes_buf[i] != nullptr) {
|
|
continue;
|
|
}
|
|
|
|
ensure_final_attribute(curves, request.attribute_name, i, cache.eval_cache);
|
|
if (cache.eval_cache.proc_attributes_point_domain[i]) {
|
|
need_tf_update = true;
|
|
}
|
|
}
|
|
|
|
return need_tf_update;
|
|
}
|
|
|
|
static void request_attribute(Curves &curves, const char *name)
|
|
{
|
|
CurvesBatchCache &cache = get_batch_cache(curves);
|
|
CurvesEvalFinalCache &final_cache = cache.eval_cache.final;
|
|
|
|
DRW_Attributes attributes{};
|
|
|
|
bke::CurvesGeometry &curves_geometry = curves.geometry.wrap();
|
|
if (!curves_geometry.attributes().contains(name)) {
|
|
return;
|
|
}
|
|
drw_attributes_add_request(&attributes, name);
|
|
|
|
drw_attributes_merge(&final_cache.attr_used, &attributes, cache.render_mutex);
|
|
}
|
|
|
|
void drw_curves_get_attribute_sampler_name(const StringRef layer_name, char r_sampler_name[32])
|
|
{
|
|
char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
|
|
GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
|
|
/* Attributes use auto-name. */
|
|
BLI_snprintf(r_sampler_name, 32, "a%s", attr_safe_name);
|
|
}
|
|
|
|
bool curves_ensure_procedural_data(Curves *curves_id,
|
|
CurvesEvalCache **r_cache,
|
|
const GPUMaterial *gpu_material,
|
|
const int subdiv,
|
|
const int thickness_res)
|
|
{
|
|
const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
|
|
bool need_ft_update = false;
|
|
|
|
CurvesBatchCache &cache = get_batch_cache(*curves_id);
|
|
CurvesEvalCache &eval_cache = cache.eval_cache;
|
|
|
|
if (eval_cache.final.hair_subdiv != subdiv || eval_cache.final.thickres != thickness_res) {
|
|
/* If the subdivision or indexing settings have changed, the evaluation cache is cleared. */
|
|
clear_final_data(eval_cache.final);
|
|
eval_cache.final.hair_subdiv = subdiv;
|
|
eval_cache.final.thickres = thickness_res;
|
|
}
|
|
|
|
eval_cache.curves_num = curves.curves_num();
|
|
eval_cache.points_num = curves.points_num();
|
|
|
|
const int steps = 3; /* TODO: don't hard-code? */
|
|
eval_cache.final.resolution = 1 << (steps + subdiv);
|
|
|
|
/* Refreshed on combing and simulation. */
|
|
if (eval_cache.proc_point_buf == nullptr || DRW_vbo_requested(eval_cache.proc_point_buf)) {
|
|
create_points_position_time_vbo(curves, eval_cache);
|
|
need_ft_update = true;
|
|
}
|
|
|
|
/* Refreshed if active layer or custom data changes. */
|
|
if (eval_cache.proc_strand_buf == nullptr) {
|
|
create_curve_offsets_vbos(curves.points_by_curve(), eval_cache);
|
|
}
|
|
|
|
/* Refreshed only on subdiv count change. */
|
|
if (eval_cache.final.proc_buf == nullptr) {
|
|
alloc_final_points_vbo(eval_cache);
|
|
need_ft_update = true;
|
|
}
|
|
|
|
if (eval_cache.final.proc_hairs == nullptr) {
|
|
calc_final_indices(curves, eval_cache, thickness_res);
|
|
}
|
|
eval_cache.final.thickres = thickness_res;
|
|
|
|
need_ft_update |= ensure_attributes(*curves_id, cache, gpu_material);
|
|
|
|
*r_cache = &eval_cache;
|
|
return need_ft_update;
|
|
}
|
|
|
|
void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode)
|
|
{
|
|
CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
|
|
if (cache == nullptr) {
|
|
return;
|
|
}
|
|
switch (mode) {
|
|
case BKE_CURVES_BATCH_DIRTY_ALL:
|
|
cache->is_dirty = true;
|
|
break;
|
|
default:
|
|
BLI_assert_unreachable();
|
|
}
|
|
}
|
|
|
|
void DRW_curves_batch_cache_validate(Curves *curves)
|
|
{
|
|
if (!batch_cache_is_dirty(*curves)) {
|
|
clear_batch_cache(*curves);
|
|
init_batch_cache(*curves);
|
|
}
|
|
}
|
|
|
|
void DRW_curves_batch_cache_free(Curves *curves)
|
|
{
|
|
clear_batch_cache(*curves);
|
|
CurvesBatchCache *batch_cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
|
|
MEM_delete(batch_cache);
|
|
curves->batch_cache = nullptr;
|
|
}
|
|
|
|
void DRW_curves_batch_cache_free_old(Curves *curves, int ctime)
|
|
{
|
|
CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
|
|
if (cache == nullptr) {
|
|
return;
|
|
}
|
|
|
|
bool do_discard = false;
|
|
|
|
CurvesEvalFinalCache &final_cache = cache->eval_cache.final;
|
|
|
|
if (drw_attributes_overlap(&final_cache.attr_used_over_time, &final_cache.attr_used)) {
|
|
final_cache.last_attr_matching_time = ctime;
|
|
}
|
|
|
|
if (ctime - final_cache.last_attr_matching_time > U.vbotimeout) {
|
|
do_discard = true;
|
|
}
|
|
|
|
drw_attributes_clear(&final_cache.attr_used_over_time);
|
|
|
|
if (do_discard) {
|
|
discard_attributes(cache->eval_cache);
|
|
}
|
|
}
|
|
|
|
gpu::Batch *DRW_curves_batch_cache_get_edit_points(Curves *curves)
|
|
{
|
|
CurvesBatchCache &cache = get_batch_cache(*curves);
|
|
return DRW_batch_request(&cache.edit_points);
|
|
}
|
|
|
|
gpu::Batch *DRW_curves_batch_cache_get_sculpt_curves_cage(Curves *curves)
|
|
{
|
|
CurvesBatchCache &cache = get_batch_cache(*curves);
|
|
return DRW_batch_request(&cache.sculpt_cage);
|
|
}
|
|
|
|
gpu::Batch *DRW_curves_batch_cache_get_edit_curves_handles(Curves *curves)
|
|
{
|
|
CurvesBatchCache &cache = get_batch_cache(*curves);
|
|
return DRW_batch_request(&cache.edit_handles);
|
|
}
|
|
|
|
gpu::Batch *DRW_curves_batch_cache_get_edit_curves_lines(Curves *curves)
|
|
{
|
|
CurvesBatchCache &cache = get_batch_cache(*curves);
|
|
return DRW_batch_request(&cache.edit_curves_lines);
|
|
}
|
|
|
|
gpu::VertBuf **DRW_curves_texture_for_evaluated_attribute(Curves *curves,
|
|
const char *name,
|
|
bool *r_is_point_domain)
|
|
{
|
|
CurvesBatchCache &cache = get_batch_cache(*curves);
|
|
CurvesEvalFinalCache &final_cache = cache.eval_cache.final;
|
|
|
|
request_attribute(*curves, name);
|
|
|
|
int request_i = -1;
|
|
for (const int i : IndexRange(final_cache.attr_used.num_requests)) {
|
|
if (STREQ(final_cache.attr_used.requests[i].attribute_name, name)) {
|
|
request_i = i;
|
|
break;
|
|
}
|
|
}
|
|
if (request_i == -1) {
|
|
*r_is_point_domain = false;
|
|
return nullptr;
|
|
}
|
|
if (cache.eval_cache.proc_attributes_point_domain[request_i]) {
|
|
*r_is_point_domain = true;
|
|
return &final_cache.attributes_buf[request_i];
|
|
}
|
|
*r_is_point_domain = false;
|
|
return &cache.eval_cache.proc_attributes_buf[request_i];
|
|
}
|
|
|
|
static void create_edit_points_position_vbo(
|
|
const bke::CurvesGeometry &curves,
|
|
const bke::crazyspace::GeometryDeformation & /*deformation*/,
|
|
CurvesBatchCache &cache)
|
|
{
|
|
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
|
"pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
|
|
|
|
/* 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());
|
|
cache.edit_curves_lines_pos->data<float3>().copy_from(positions);
|
|
}
|
|
|
|
void DRW_curves_batch_cache_create_requested(Object *ob)
|
|
{
|
|
Curves &curves_id = DRW_object_get_data_for_drawing<Curves>(*ob);
|
|
Object *ob_orig = DEG_get_original(ob);
|
|
if (ob_orig == nullptr) {
|
|
return;
|
|
}
|
|
const Curves &curves_orig_id = DRW_object_get_data_for_drawing<Curves>(*ob_orig);
|
|
|
|
draw::CurvesBatchCache &cache = draw::get_batch_cache(curves_id);
|
|
const bke::CurvesGeometry &curves_orig = curves_orig_id.geometry.wrap();
|
|
|
|
bool is_edit_data_needed = false;
|
|
|
|
if (DRW_batch_requested(cache.edit_points, GPU_PRIM_POINTS)) {
|
|
DRW_vbo_request(cache.edit_points, &cache.edit_points_pos);
|
|
DRW_vbo_request(cache.edit_points, &cache.edit_points_data);
|
|
DRW_vbo_request(cache.edit_points, &cache.edit_points_selection);
|
|
is_edit_data_needed = true;
|
|
}
|
|
if (DRW_batch_requested(cache.sculpt_cage, GPU_PRIM_LINE_STRIP)) {
|
|
DRW_ibo_request(cache.sculpt_cage, &cache.sculpt_cage_ibo);
|
|
DRW_vbo_request(cache.sculpt_cage, &cache.edit_points_pos);
|
|
DRW_vbo_request(cache.sculpt_cage, &cache.edit_points_data);
|
|
DRW_vbo_request(cache.sculpt_cage, &cache.edit_points_selection);
|
|
is_edit_data_needed = true;
|
|
}
|
|
if (DRW_batch_requested(cache.edit_handles, GPU_PRIM_LINES)) {
|
|
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);
|
|
is_edit_data_needed = true;
|
|
}
|
|
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);
|
|
}
|
|
|
|
const OffsetIndices<int> points_by_curve = curves_orig.points_by_curve();
|
|
const VArray<bool> cyclic = curves_orig.cyclic();
|
|
|
|
const bke::crazyspace::GeometryDeformation deformation =
|
|
is_edit_data_needed || DRW_vbo_requested(cache.edit_curves_lines_pos) ?
|
|
bke::crazyspace::get_evaluated_curves_deformation(ob, *ob_orig) :
|
|
bke::crazyspace::GeometryDeformation();
|
|
|
|
if (DRW_ibo_requested(cache.sculpt_cage_ibo)) {
|
|
create_lines_ibo_no_cyclic(points_by_curve, *cache.sculpt_cage_ibo);
|
|
}
|
|
|
|
if (DRW_vbo_requested(cache.edit_curves_lines_pos)) {
|
|
create_edit_points_position_vbo(curves_orig, deformation, cache);
|
|
}
|
|
|
|
if (DRW_ibo_requested(cache.edit_curves_lines_ibo)) {
|
|
create_lines_ibo_with_cyclic(
|
|
curves_orig.evaluated_points_by_curve(), cyclic, *cache.edit_curves_lines_ibo);
|
|
}
|
|
|
|
if (!is_edit_data_needed) {
|
|
return;
|
|
}
|
|
|
|
const IndexRange curves_range = curves_orig.curves_range();
|
|
const VArray<int8_t> curve_types = curves_orig.curve_types();
|
|
const std::array<int, CURVE_TYPES_NUM> type_counts = curves_orig.curve_type_counts();
|
|
const bke::AttributeAccessor attributes = curves_orig.attributes();
|
|
|
|
IndexMaskMemory memory;
|
|
const IndexMask catmull_rom_curves = bke::curves::indices_for_type(
|
|
curve_types, type_counts, CURVE_TYPE_CATMULL_ROM, curves_range, memory);
|
|
const IndexMask poly_curves = bke::curves::indices_for_type(
|
|
curve_types, type_counts, CURVE_TYPE_POLY, curves_range, memory);
|
|
const IndexMask bezier_curves = bke::curves::indices_for_type(
|
|
curve_types, type_counts, CURVE_TYPE_BEZIER, curves_range, memory);
|
|
const IndexMask nurbs_curves = bke::curves::indices_for_type(
|
|
curve_types, type_counts, CURVE_TYPE_NURBS, curves_range, memory);
|
|
|
|
Array<int> bezier_point_offset_data(bezier_curves.size() + 1);
|
|
const OffsetIndices<int> bezier_offsets = offset_indices::gather_selected_offsets(
|
|
points_by_curve, bezier_curves, bezier_point_offset_data);
|
|
|
|
if (DRW_vbo_requested(cache.edit_points_pos)) {
|
|
create_edit_points_position(curves_orig,
|
|
points_by_curve,
|
|
bezier_curves,
|
|
bezier_offsets,
|
|
deformation,
|
|
*cache.edit_points_pos);
|
|
}
|
|
if (DRW_vbo_requested(cache.edit_points_data)) {
|
|
create_edit_points_data(points_by_curve,
|
|
catmull_rom_curves,
|
|
poly_curves,
|
|
bezier_curves,
|
|
nurbs_curves,
|
|
bezier_offsets,
|
|
curves_orig,
|
|
*cache.edit_points_data);
|
|
}
|
|
if (DRW_vbo_requested(cache.edit_points_selection)) {
|
|
create_edit_points_selection(
|
|
points_by_curve, bezier_curves, bezier_offsets, attributes, *cache.edit_points_selection);
|
|
}
|
|
if (DRW_ibo_requested(cache.edit_handles_ibo)) {
|
|
calc_edit_handles_ibo(points_by_curve,
|
|
catmull_rom_curves,
|
|
poly_curves,
|
|
bezier_curves,
|
|
nurbs_curves,
|
|
bezier_offsets,
|
|
cyclic,
|
|
*cache.edit_handles_ibo);
|
|
}
|
|
}
|
|
|
|
} // namespace blender::draw
|