Curves: Changes to the new curves data-block

This patch refactors the "Hair" data-block, which will soon be renamed
to "Curves". The larger change is switching from an array of `HairCurve`
to find indices in the points array to simply storing an array of offsets.
Using a single integer instead of two halves the amount of memory for that
particular array.

Besides that, there are some other changes in this patch:
- Split the data-structure to a separate `CurveGeometry`
  DNA struct so it is usable for grease pencil too.
- Update naming to be more aligned with newer code and the style guide.
- Add direct access to some arrays in RNA
-- Radius is now retrieved as a regular attribute in Cycles.
-- `HairPoint` has been renamed to `CurvePoint`
-- `HairCurve` has been renamed to `CurveSlice`
- Add comments to the struct in DNA.

The next steps are renaming `Hair` -> `Curves`, and adding support
for other curve types: Bezier, Poly, and NURBS.

Ref T95355

Differential Revision: https://developer.blender.org/D13987
This commit is contained in:
Hans Goudey 2022-02-03 10:49:51 -06:00
parent 2f591adc1a
commit f59767ff97
Notes: blender-bot 2023-02-14 06:57:56 +01:00
Referenced by commit fe1816f67f, Curves: Rename "Hair" types, variables, and functions to "Curves"
Referenced by issue #95355, New Curves data block
8 changed files with 359 additions and 185 deletions

View File

@ -14,6 +14,8 @@
* limitations under the License.
*/
#include <optional>
#include "blender/sync.h"
#include "blender/util.h"
@ -625,14 +627,35 @@ void BlenderSync::sync_particle_hair(
}
#ifdef WITH_HAIR_NODES
static float4 hair_point_as_float4(BL::HairPoint b_point)
static std::optional<BL::FloatAttribute> find_curves_radius_attribute(BL::Hair b_hair)
{
float4 mP = float3_to_float4(get_float3(b_point.co()));
mP.w = b_point.radius();
for (BL::Attribute &b_attribute : b_hair.attributes) {
if (b_attribute.name() != "radius") {
continue;
}
if (b_attribute.domain() != BL::Attribute::domain_POINT) {
continue;
}
if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) {
continue;
}
return BL::FloatAttribute{b_attribute};
}
return std::nullopt;
}
static float4 hair_point_as_float4(BL::Hair b_hair,
std::optional<BL::FloatAttribute> b_attr_radius,
const int index)
{
float4 mP = float3_to_float4(get_float3(b_hair.position_data[index].vector()));
mP.w = b_attr_radius ? b_attr_radius->data[index].value() : 0.0f;
return mP;
}
static float4 interpolate_hair_points(BL::Hair b_hair,
std::optional<BL::FloatAttribute> b_attr_radius,
const int first_point_index,
const int num_points,
const float step)
@ -641,8 +664,8 @@ static float4 interpolate_hair_points(BL::Hair b_hair,
const int point_a = clamp((int)curve_t, 0, num_points - 1);
const int point_b = min(point_a + 1, num_points - 1);
const float t = curve_t - (float)point_a;
return lerp(hair_point_as_float4(b_hair.points[first_point_index + point_a]),
hair_point_as_float4(b_hair.points[first_point_index + point_b]),
return lerp(hair_point_as_float4(b_hair, b_attr_radius, first_point_index + point_a),
hair_point_as_float4(b_hair, b_attr_radius, first_point_index + point_b),
t);
}
@ -671,12 +694,14 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
hair->reserve_curves(num_curves, num_keys);
std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_hair);
/* Export curves and points. */
vector<float> points_length;
for (BL::HairCurve &b_curve : b_hair.curves) {
const int first_point_index = b_curve.first_point_index();
const int num_points = b_curve.num_points();
for (int i = 0; i < num_curves; i++) {
const int first_point_index = b_hair.curve_offset_data[i].value();
const int num_points = b_hair.curve_offset_data[i + 1].value() - first_point_index;
float3 prev_co = zero_float3();
float length = 0.0f;
@ -687,10 +712,9 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Position and radius. */
for (int i = 0; i < num_points; i++) {
BL::HairPoint b_point = b_hair.points[first_point_index + i];
const float3 co = get_float3(b_point.co());
const float radius = b_point.radius();
const float3 co = get_float3(b_hair.position_data[first_point_index + i].vector());
const float radius = b_attr_radius ? b_attr_radius->data[first_point_index + i].value() :
0.0f;
hair->add_curve_key(co, radius);
if (attr_intercept) {
@ -715,7 +739,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Random number per curve. */
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(b_curve.index(), 0));
attr_random->add(hash_uint2_to_float(i, 0));
}
/* Curve. */
@ -737,14 +761,17 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
/* Export motion keys. */
const int num_keys = hair->get_curve_keys().size();
const int num_curves = b_hair.curves.length();
float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
bool have_motion = false;
int num_motion_keys = 0;
int curve_index = 0;
for (BL::HairCurve &b_curve : b_hair.curves) {
const int first_point_index = b_curve.first_point_index();
const int num_points = b_curve.num_points();
std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_hair);
for (int i = 0; i < num_curves; i++) {
const int first_point_index = b_hair.curve_offset_data[i].value();
const int num_points = b_hair.curve_offset_data[i + 1].value() - first_point_index;
Hair::Curve curve = hair->get_curve(curve_index);
curve_index++;
@ -755,7 +782,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
int point_index = first_point_index + i;
if (point_index < num_keys) {
mP[num_motion_keys] = hair_point_as_float4(b_hair.points[point_index]);
mP[num_motion_keys] = hair_point_as_float4(b_hair, b_attr_radius, point_index);
num_motion_keys++;
if (!have_motion) {
@ -774,7 +801,8 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
const float step_size = curve.num_keys > 1 ? 1.0f / (curve.num_keys - 1) : 0.0f;
for (int i = 0; i < curve.num_keys; i++) {
const float step = i * step_size;
mP[num_motion_keys] = interpolate_hair_points(b_hair, first_point_index, num_points, step);
mP[num_motion_keys] = interpolate_hair_points(
b_hair, b_attr_radius, first_point_index, num_points, step);
num_motion_keys++;
}
have_motion = true;

View File

@ -90,10 +90,10 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
}
case ID_HA: {
Hair *hair = (Hair *)id;
info[ATTR_DOMAIN_POINT].customdata = &hair->pdata;
info[ATTR_DOMAIN_POINT].length = hair->totpoint;
info[ATTR_DOMAIN_CURVE].customdata = &hair->cdata;
info[ATTR_DOMAIN_CURVE].length = hair->totcurve;
info[ATTR_DOMAIN_POINT].customdata = &hair->geometry.point_data;
info[ATTR_DOMAIN_POINT].length = hair->geometry.point_size;
info[ATTR_DOMAIN_CURVE].customdata = &hair->geometry.curve_data;
info[ATTR_DOMAIN_CURVE].length = hair->geometry.curve_size;
break;
}
default:

View File

@ -1793,10 +1793,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(float[3]), "vec3f", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 44: CD_RADIUS */
{sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 45: CD_HAIRCURVE */
{sizeof(HairCurve), "HairCurve", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 46: CD_HAIRMAPPING */
{sizeof(HairMapping), "HairMapping", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 45: CD_HAIRCURVE */ /* DEPRECATED */
{-1, "HairCurve", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 46: CD_HAIRMAPPING */ /* DEPRECATED */
{-1, "HairMapping", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 47: CD_PROP_COLOR */
{sizeof(MPropCol),
"MPropCol",

View File

@ -28,6 +28,7 @@
#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vec_types.hh"
@ -54,6 +55,8 @@
#include "BLO_read_write.h"
using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::RandomNumberGenerator;
static const char *HAIR_ATTR_POSITION = "position";
@ -70,14 +73,22 @@ static void hair_init_data(ID *id)
MEMCPY_STRUCT_AFTER(hair, DNA_struct_default_get(Hair), id);
CustomData_reset(&hair->pdata);
CustomData_reset(&hair->cdata);
CustomData_reset(&hair->geometry.point_data);
CustomData_reset(&hair->geometry.curve_data);
CustomData_add_layer_named(&hair->geometry.point_data,
CD_PROP_FLOAT3,
CD_CALLOC,
nullptr,
hair->geometry.point_size,
HAIR_ATTR_POSITION);
CustomData_add_layer_named(&hair->geometry.point_data,
CD_PROP_FLOAT,
CD_CALLOC,
nullptr,
hair->geometry.point_size,
HAIR_ATTR_RADIUS);
CustomData_add_layer_named(
&hair->pdata, CD_PROP_FLOAT3, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_POSITION);
CustomData_add_layer_named(
&hair->pdata, CD_PROP_FLOAT, CD_CALLOC, nullptr, hair->totpoint, HAIR_ATTR_RADIUS);
CustomData_add_layer(&hair->cdata, CD_HAIRCURVE, CD_CALLOC, nullptr, hair->totcurve);
BKE_hair_update_customdata_pointers(hair);
hair_random(hair);
@ -89,11 +100,24 @@ static void hair_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, co
const Hair *hair_src = (const Hair *)id_src;
hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat));
hair_dst->geometry.point_size = hair_src->geometry.point_size;
hair_dst->geometry.curve_size = hair_src->geometry.curve_size;
const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, alloc_type, hair_dst->totpoint);
CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, alloc_type, hair_dst->totcurve);
CustomData_copy(&hair_src->geometry.point_data,
&hair_dst->geometry.point_data,
CD_MASK_ALL,
alloc_type,
hair_dst->geometry.point_size);
CustomData_copy(&hair_src->geometry.curve_data,
&hair_dst->geometry.curve_data,
CD_MASK_ALL,
alloc_type,
hair_dst->geometry.curve_size);
BKE_hair_update_customdata_pointers(hair_dst);
hair_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(hair_src->geometry.offsets));
hair_dst->batch_cache = nullptr;
}
@ -104,8 +128,10 @@ static void hair_free_data(ID *id)
BKE_hair_batch_cache_free(hair);
CustomData_free(&hair->pdata, hair->totpoint);
CustomData_free(&hair->cdata, hair->totcurve);
CustomData_free(&hair->geometry.point_data, hair->geometry.point_size);
CustomData_free(&hair->geometry.curve_data, hair->geometry.curve_size);
MEM_SAFE_FREE(hair->geometry.offsets);
MEM_SAFE_FREE(hair->mat);
}
@ -124,16 +150,30 @@ static void hair_blend_write(BlendWriter *writer, ID *id, const void *id_address
CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE];
CustomDataLayer *clayers = nullptr, clayers_buff[CD_TEMP_CHUNK_SIZE];
CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
CustomData_blend_write_prepare(
&hair->geometry.point_data, &players, players_buff, ARRAY_SIZE(players_buff));
CustomData_blend_write_prepare(
&hair->geometry.curve_data, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
/* Write LibData */
BLO_write_id_struct(writer, Hair, id_address, &hair->id);
BKE_id_blend_write(writer, &hair->id);
/* Direct data */
CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id);
CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id);
CustomData_blend_write(writer,
&hair->geometry.point_data,
players,
hair->geometry.point_size,
CD_MASK_ALL,
&hair->id);
CustomData_blend_write(writer,
&hair->geometry.curve_data,
clayers,
hair->geometry.curve_size,
CD_MASK_ALL,
&hair->id);
BLO_write_int32_array(writer, hair->geometry.curve_size + 1, hair->geometry.offsets);
BLO_write_pointer_array(writer, hair->totcol, hair->mat);
if (hair->adt) {
@ -156,10 +196,12 @@ static void hair_blend_read_data(BlendDataReader *reader, ID *id)
BKE_animdata_blend_read_data(reader, hair->adt);
/* Geometry */
CustomData_blend_read(reader, &hair->pdata, hair->totpoint);
CustomData_blend_read(reader, &hair->cdata, hair->totcurve);
CustomData_blend_read(reader, &hair->geometry.point_data, hair->geometry.point_size);
CustomData_blend_read(reader, &hair->geometry.curve_data, hair->geometry.point_size);
BKE_hair_update_customdata_pointers(hair);
BLO_read_int32_array(reader, hair->geometry.curve_size + 1, &hair->geometry.offsets);
/* Materials */
BLO_read_pointer_array(reader, (void **)&hair->mat);
}
@ -212,38 +254,48 @@ IDTypeInfo IDType_ID_HA = {
static void hair_random(Hair *hair)
{
CurvesGeometry &geometry = hair->geometry;
const int numpoints = 8;
hair->totcurve = 500;
hair->totpoint = hair->totcurve * numpoints;
geometry.curve_size = 500;
CustomData_realloc(&hair->pdata, hair->totpoint);
CustomData_realloc(&hair->cdata, hair->totcurve);
geometry.curve_size = 500;
geometry.point_size = geometry.curve_size * numpoints;
hair->geometry.offsets = (int *)MEM_calloc_arrayN(
hair->geometry.curve_size + 1, sizeof(int), __func__);
CustomData_realloc(&geometry.point_data, geometry.point_size);
CustomData_realloc(&geometry.curve_data, geometry.curve_size);
BKE_hair_update_customdata_pointers(hair);
MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1};
MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size};
MutableSpan<float> radii{geometry.radius, geometry.point_size};
for (const int i : offsets.index_range()) {
geometry.offsets[i] = numpoints * i;
}
RandomNumberGenerator rng;
for (int i = 0; i < hair->totcurve; i++) {
HairCurve *curve = &hair->curves[i];
curve->firstpoint = i * numpoints;
curve->numpoints = numpoints;
for (int i = 0; i < geometry.curve_size; i++) {
const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
MutableSpan<float3> curve_positions = positions.slice(curve_range);
MutableSpan<float> curve_radii = radii.slice(curve_range);
const float theta = 2.0f * M_PI * rng.get_float();
const float phi = saacosf(2.0f * rng.get_float() - 1.0f);
float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)};
blender::math::normalize(no);
no = blender::math::normalize(no);
float(*curve_positions)[3] = hair->co + curve->firstpoint;
float *curve_radii = hair->radius + curve->firstpoint;
float3 co = no;
for (int key = 0; key < numpoints; key++) {
float t = key / (float)(numpoints - 1);
copy_v3_v3(curve_positions[key], co);
curve_positions[key] = co;
curve_radii[key] = 0.02f * (1.0f - t);
float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f;
co += (offset + no) / numpoints;
}
}
@ -271,9 +323,9 @@ BoundBox *BKE_hair_boundbox_get(Object *ob)
float min[3], max[3];
INIT_MINMAX(min, max);
float(*hair_co)[3] = hair->co;
float *hair_radius = hair->radius;
for (int a = 0; a < hair->totpoint; a++) {
float(*hair_co)[3] = hair->geometry.position;
float *hair_radius = hair->geometry.radius;
for (int a = 0; a < hair->geometry.point_size; a++) {
float *co = hair_co[a];
float radius = (hair_radius) ? hair_radius[a] : 0.0f;
const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
@ -290,12 +342,10 @@ BoundBox *BKE_hair_boundbox_get(Object *ob)
void BKE_hair_update_customdata_pointers(Hair *hair)
{
hair->co = (float(*)[3])CustomData_get_layer_named(
&hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION);
hair->radius = (float *)CustomData_get_layer_named(
&hair->pdata, CD_PROP_FLOAT, HAIR_ATTR_RADIUS);
hair->curves = (HairCurve *)CustomData_get_layer(&hair->cdata, CD_HAIRCURVE);
hair->mapping = (HairMaping *)CustomData_get_layer(&hair->cdata, CD_HAIRMAPPING);
hair->geometry.position = (float(*)[3])CustomData_get_layer_named(
&hair->geometry.point_data, CD_PROP_FLOAT3, HAIR_ATTR_POSITION);
hair->geometry.radius = (float *)CustomData_get_layer_named(
&hair->geometry.point_data, CD_PROP_FLOAT, HAIR_ATTR_RADIUS);
}
bool BKE_hair_customdata_required(Hair *UNUSED(hair), CustomDataLayer *layer)
@ -313,10 +363,18 @@ Hair *BKE_hair_new_for_eval(const Hair *hair_src, int totpoint, int totcurve)
hair_dst->mat = static_cast<Material **>(MEM_dupallocN(hair_src->mat));
hair_dst->totcol = hair_src->totcol;
hair_dst->totpoint = totpoint;
hair_dst->totcurve = totcurve;
CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint);
CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, CD_CALLOC, totcurve);
hair_dst->geometry.point_size = totpoint;
hair_dst->geometry.curve_size = totcurve;
CustomData_copy(&hair_src->geometry.point_data,
&hair_dst->geometry.point_data,
CD_MASK_ALL,
CD_CALLOC,
totpoint);
CustomData_copy(&hair_src->geometry.curve_data,
&hair_dst->geometry.curve_data,
CD_MASK_ALL,
CD_CALLOC,
totcurve);
BKE_hair_update_customdata_pointers(hair_dst);
return hair_dst;
@ -368,12 +426,14 @@ static Hair *hair_evaluate_modifiers(struct Depsgraph *depsgraph,
}
/* Ensure we are not overwriting referenced data. */
CustomData_duplicate_referenced_layer_named(
&hair->pdata, CD_PROP_FLOAT3, HAIR_ATTR_POSITION, hair->totpoint);
CustomData_duplicate_referenced_layer_named(&hair->geometry.point_data,
CD_PROP_FLOAT3,
HAIR_ATTR_POSITION,
hair->geometry.point_size);
BKE_hair_update_customdata_pointers(hair);
/* Created deformed coordinates array on demand. */
mti->deformVerts(md, &mectx, nullptr, hair->co, hair->totpoint);
mti->deformVerts(md, &mectx, nullptr, hair->geometry.position, hair->geometry.point_size);
}
}

View File

@ -1122,7 +1122,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Hair and PointCloud attributes. */
for (Hair *hair = bmain->hairs.first; hair != NULL; hair = hair->id.next) {
do_versions_point_attributes(&hair->pdata);
do_versions_point_attributes(&hair->geometry.point_data);
}
for (PointCloud *pointcloud = bmain->pointclouds.first; pointcloud != NULL;
pointcloud = pointcloud->id.next) {
@ -1424,7 +1424,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Hair and PointCloud attributes names. */
LISTBASE_FOREACH (Hair *, hair, &bmain->hairs) {
do_versions_point_attribute_names(&hair->pdata);
do_versions_point_attribute_names(&hair->geometry.point_data);
}
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
do_versions_point_attribute_names(&pointcloud->pdata);

View File

@ -29,7 +29,10 @@
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vec_types.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
#include "DNA_hair_types.h"
@ -44,6 +47,10 @@
#include "draw_cache_impl.h" /* own include */
#include "draw_hair_private.h" /* own include */
using blender::float3;
using blender::IndexRange;
using blender::Span;
static void hair_batch_cache_clear(Hair *hair);
/* ---------------------------------------------------------------------- */
@ -131,17 +138,9 @@ static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache)
return;
}
hair_cache->strands_len = 0;
hair_cache->elems_len = 0;
hair_cache->point_len = 0;
HairCurve *curve = hair->curves;
int num_curves = hair->totcurve;
for (int i = 0; i < num_curves; i++, curve++) {
hair_cache->strands_len++;
hair_cache->elems_len += curve->numpoints + 1;
hair_cache->point_len += curve->numpoints;
}
hair_cache->strands_len = hair->geometry.curve_size;
hair_cache->elems_len = hair->geometry.point_size + hair->geometry.curve_size;
hair_cache->point_len = hair->geometry.point_size;
}
static void hair_batch_cache_fill_segments_proc_pos(Hair *hair,
@ -149,30 +148,36 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair,
GPUVertBufRaw *length_step)
{
/* TODO: use hair radius layer if available. */
HairCurve *curve = hair->curves;
int num_curves = hair->totcurve;
for (int i = 0; i < num_curves; i++, curve++) {
float(*curve_co)[3] = hair->co + curve->firstpoint;
const int curve_size = hair->geometry.curve_size;
Span<int> offsets{hair->geometry.offsets, hair->geometry.curve_size + 1};
Span<float3> positions{(float3 *)hair->geometry.position, hair->geometry.point_size};
for (const int i : IndexRange(curve_size)) {
const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
Span<float3> spline_positions = positions.slice(curve_range);
float total_len = 0.0f;
float *co_prev = nullptr, *seg_data_first;
for (int j = 0; j < curve->numpoints; j++) {
float *seg_data_first;
for (const int i_spline : spline_positions.index_range()) {
float *seg_data = (float *)GPU_vertbuf_raw_step(attr_step);
copy_v3_v3(seg_data, curve_co[j]);
if (co_prev) {
total_len += len_v3v3(co_prev, curve_co[j]);
}
else {
copy_v3_v3(seg_data, spline_positions[i_spline]);
if (i_spline == 0) {
seg_data_first = seg_data;
}
else {
total_len += blender::math::distance(spline_positions[i_spline - 1],
spline_positions[i_spline]);
}
seg_data[3] = total_len;
co_prev = curve_co[j];
}
/* Assign length value. */
*(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) {
for ([[maybe_unused]] const int i_spline : spline_positions.index_range()) {
seg_data_first[3] /= total_len;
seg_data_first += 4;
}
}
}
@ -226,11 +231,14 @@ static void hair_batch_cache_fill_strands_data(Hair *hair,
GPUVertBufRaw *data_step,
GPUVertBufRaw *seg_step)
{
HairCurve *curve = hair->curves;
int num_curves = hair->totcurve;
for (int i = 0; i < num_curves; i++, curve++) {
*(uint *)GPU_vertbuf_raw_step(data_step) = curve->firstpoint;
*(ushort *)GPU_vertbuf_raw_step(seg_step) = curve->numpoints - 1;
const int curve_size = hair->geometry.curve_size;
Span<int> offsets{hair->geometry.offsets, hair->geometry.curve_size + 1};
for (const int i : IndexRange(curve_size)) {
const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
*(uint *)GPU_vertbuf_raw_step(data_step) = curve_range.start();
*(ushort *)GPU_vertbuf_raw_step(seg_step) = curve_range.size() - 1;
}
}
@ -289,10 +297,11 @@ static void hair_batch_cache_fill_segments_indices(Hair *hair,
const int res,
GPUIndexBufBuilder *elb)
{
HairCurve *curve = hair->curves;
int num_curves = hair->totcurve;
const int curve_size = hair->geometry.curve_size;
uint curr_point = 0;
for (int i = 0; i < num_curves; i++, curve++) {
for ([[maybe_unused]] const int i : IndexRange(curve_size)) {
for (int k = 0; k < res; k++) {
GPU_indexbuf_add_generic_vert(elb, curr_point++);
}

View File

@ -27,48 +27,71 @@
extern "C" {
#endif
typedef struct HairCurve {
/* Index of first point of hair curve. */
int firstpoint;
/* Number of points in hair curve, must be 2 or higher. */
int numpoints;
} HairCurve;
/**
* A reusable data structure for geometry consisting of many curves. All control point data is
* stored contiguously for better efficiency. Data for each curve is stored as a slice of the
* main #point_data array.
*
* The data structure is meant to be embedded in other data-blocks to allow reusing
* curve-processing algorithms for multiple Blender data-block types.
*/
typedef struct CurvesGeometry {
/**
* A runtime pointer to the "position" attribute data.
* \note This data is owned by #point_data.
*/
float (*position)[3];
/**
* A runtime pointer to the "radius" attribute data.
* \note This data is owned by #point_data.
*/
float *radius;
/* Hair attachment to a mesh.
* TODO: attach to tessellated triangles or polygons?
* TODO: what type of interpolation to use for uv? */
typedef struct HairMapping {
float uv[2];
int poly;
} HairMapping;
/**
* The start index of each curve in the point data. The size of each curve can be calculated by
* subtracting the offset from the next offset. That is valid even for the last curve because
* this array is allocated with a length one larger than the number of splines.
*
* \note This is *not* stored in #CustomData because its size is one larger than #curve_data.
*/
int *offsets;
/**
* All attributes stored on control points (#ATTR_DOMAIN_POINT).
*/
CustomData point_data;
/**
* All attributes stored on curves (#ATTR_DOMAIN_CURVE).
*/
CustomData curve_data;
/**
* The total number of control points in all curves.
*/
int point_size;
/**
* The number of curves in the data-block.
*/
int curve_size;
} CurvesGeometry;
typedef struct Hair {
ID id;
struct AnimData *adt; /* animation data (must be immediately after id) */
/* Animation data (must be immediately after id). */
struct AnimData *adt;
CurvesGeometry geometry;
int flag;
int _pad1[1];
/* Geometry */
float (*co)[3];
float *radius;
struct HairCurve *curves;
struct HairMaping *mapping;
int totpoint;
int totcurve;
/* Custom Data */
struct CustomData pdata;
struct CustomData cdata;
int attributes_active_index;
int _pad3;
/* Material */
/* Materials. */
struct Material **mat;
short totcol;
short _pad2[3];
/* Draw Cache */
/* Draw Cache. */
void *batch_cache;
} Hair;

View File

@ -47,71 +47,97 @@ static Hair *rna_hair(PointerRNA *ptr)
return (Hair *)ptr->owner_id;
}
static int rna_HairPoint_index_get(PointerRNA *ptr)
static int rna_Hair_curve_offset_data_length(PointerRNA *ptr)
{
const Hair *curves = rna_hair(ptr);
return curves->geometry.curve_size + 1;
}
static void rna_Hair_curve_offset_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
const Hair *curves = rna_hair(ptr);
rna_iterator_array_begin(iter,
(void *)curves->geometry.offsets,
sizeof(int),
curves->geometry.curve_size + 1,
false,
NULL);
}
static int rna_CurvePoint_index_get(PointerRNA *ptr)
{
const Hair *hair = rna_hair(ptr);
const float(*co)[3] = ptr->data;
return (int)(co - hair->co);
return (int)(co - hair->geometry.position);
}
static void rna_HairPoint_location_get(PointerRNA *ptr, float value[3])
static void rna_CurvePoint_location_get(PointerRNA *ptr, float value[3])
{
copy_v3_v3(value, (const float *)ptr->data);
}
static void rna_HairPoint_location_set(PointerRNA *ptr, const float value[3])
static void rna_CurvePoint_location_set(PointerRNA *ptr, const float value[3])
{
copy_v3_v3((float *)ptr->data, value);
}
static float rna_HairPoint_radius_get(PointerRNA *ptr)
static float rna_CurvePoint_radius_get(PointerRNA *ptr)
{
const Hair *hair = rna_hair(ptr);
if (hair->radius == NULL) {
if (hair->geometry.radius == NULL) {
return 0.0f;
}
const float(*co)[3] = ptr->data;
return hair->radius[co - hair->co];
return hair->geometry.radius[co - hair->geometry.position];
}
static void rna_HairPoint_radius_set(PointerRNA *ptr, float value)
static void rna_CurvePoint_radius_set(PointerRNA *ptr, float value)
{
const Hair *hair = rna_hair(ptr);
if (hair->radius == NULL) {
if (hair->geometry.radius == NULL) {
return;
}
const float(*co)[3] = ptr->data;
hair->radius[co - hair->co] = value;
hair->geometry.radius[co - hair->geometry.position] = value;
}
static char *rna_HairPoint_path(PointerRNA *ptr)
static char *rna_CurvePoint_path(PointerRNA *ptr)
{
return BLI_sprintfN("points[%d]", rna_HairPoint_index_get(ptr));
return BLI_sprintfN("points[%d]", rna_CurvePoint_index_get(ptr));
}
static int rna_HairCurve_index_get(PointerRNA *ptr)
static int rna_CurveSlice_index_get(PointerRNA *ptr)
{
Hair *hair = rna_hair(ptr);
return (int)((HairCurve *)ptr->data - hair->curves);
return (int)((int *)ptr->data - hair->geometry.offsets);
}
static char *rna_HairCurve_path(PointerRNA *ptr)
static char *rna_CurveSlice_path(PointerRNA *ptr)
{
return BLI_sprintfN("curves[%d]", rna_HairCurve_index_get(ptr));
return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get(ptr));
}
static void rna_HairCurve_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
Hair *hair = rna_hair(ptr);
HairCurve *curve = ptr->data;
float(*co)[3] = hair->co + curve->firstpoint;
rna_iterator_array_begin(iter, co, sizeof(float[3]), curve->numpoints, 0, NULL);
const int *offset_ptr = (int *)ptr->data;
const int offset = *offset_ptr;
const int size = *(offset_ptr + 1) - offset;
float(*co)[3] = hair->geometry.position + *offset_ptr;
rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL);
}
static int rna_HairCurve_points_length(PointerRNA *ptr)
static int rna_CurveSlice_first_point_index_get(PointerRNA *ptr)
{
HairCurve *curve = ptr->data;
return curve->numpoints;
const int *offset_ptr = (int *)ptr->data;
return *offset_ptr;
}
static int rna_CurveSlice_points_length_get(PointerRNA *ptr)
{
const int *offset_ptr = (int *)ptr->data;
const int offset = *offset_ptr;
return *(offset_ptr + 1) - offset;
}
static void rna_Hair_update_data(struct Main *UNUSED(bmain),
@ -134,25 +160,26 @@ static void rna_def_hair_point(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "HairPoint", NULL);
RNA_def_struct_ui_text(srna, "Hair Point", "Hair curve control point");
RNA_def_struct_path_func(srna, "rna_HairPoint_path");
srna = RNA_def_struct(brna, "CurvePoint", NULL);
RNA_def_struct_ui_text(srna, "Curve Point", "Curve curve control point");
RNA_def_struct_path_func(srna, "rna_CurvePoint_path");
prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION);
prop = RNA_def_property(srna, "position", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 3);
RNA_def_property_float_funcs(
prop, "rna_HairPoint_location_get", "rna_HairPoint_location_set", NULL);
RNA_def_property_ui_text(prop, "Location", "");
prop, "rna_CurvePoint_location_get", "rna_CurvePoint_location_set", NULL);
RNA_def_property_ui_text(prop, "Position", "");
RNA_def_property_update(prop, 0, "rna_Hair_update_data");
prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_funcs(prop, "rna_HairPoint_radius_get", "rna_HairPoint_radius_set", NULL);
RNA_def_property_float_funcs(
prop, "rna_CurvePoint_radius_get", "rna_CurvePoint_radius_set", NULL);
RNA_def_property_ui_text(prop, "Radius", "");
RNA_def_property_update(prop, 0, "rna_Hair_update_data");
prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_int_funcs(prop, "rna_HairPoint_index_get", NULL, NULL);
RNA_def_property_int_funcs(prop, "rna_CurvePoint_index_get", NULL, NULL);
RNA_def_property_ui_text(prop, "Index", "Index of this points");
}
@ -161,35 +188,37 @@ static void rna_def_hair_curve(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "HairCurve", NULL);
RNA_def_struct_ui_text(srna, "Hair Curve", "Hair curve");
RNA_def_struct_path_func(srna, "rna_HairCurve_path");
srna = RNA_def_struct(brna, "CurveSlice", NULL);
RNA_def_struct_ui_text(srna, "Curve Slice", "A single curve from a curves data-block");
RNA_def_struct_path_func(srna, "rna_CurveSlice_path");
prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "HairPoint");
RNA_def_property_struct_type(prop, "CurvePoint");
RNA_def_property_ui_text(prop, "Points", "Control points of the curve");
RNA_def_property_collection_funcs(prop,
"rna_HairCurve_points_begin",
"rna_CurveSlice_points_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_HairCurve_points_length",
"rna_CurveSlice_points_length_get",
NULL,
NULL,
NULL);
/* TODO: naming consistency, editable? */
prop = RNA_def_property(srna, "first_point_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "firstpoint");
RNA_def_property_ui_text(prop, "First Point Index", "Index of the first loop of this polygon");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_int_funcs(prop, "rna_CurveSlice_first_point_index_get", NULL, NULL);
RNA_def_property_ui_text(
prop, "First Point Index", "The index of this curve's first control point");
prop = RNA_def_property(srna, "num_points", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "numpoints");
RNA_def_property_ui_text(prop, "Number of Points", "Number of loops used by this polygon");
prop = RNA_def_property(srna, "points_length", PROP_INT, PROP_UNSIGNED);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_int_funcs(prop, "rna_CurveSlice_points_length_get", NULL, NULL);
RNA_def_property_ui_text(prop, "Number of Points", "Number of control points in the curve");
prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_int_funcs(prop, "rna_HairCurve_index_get", NULL, NULL);
RNA_def_property_int_funcs(prop, "rna_CurveSlice_index_get", NULL, NULL);
RNA_def_property_ui_text(prop, "Index", "Index of this curve");
}
@ -202,20 +231,45 @@ static void rna_def_hair(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Hair", "Hair data-block for hair curves");
RNA_def_struct_ui_icon(srna, ICON_HAIR_DATA);
/* geometry */
/* Point and Curve RNA API helpers. */
prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "curves", "totcurve");
RNA_def_property_struct_type(prop, "HairCurve");
RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", "geometry.curve_size");
RNA_def_property_struct_type(prop, "CurveSlice");
RNA_def_property_ui_text(prop, "Curves", "All hair curves");
/* TODO: better solution for (*co)[3] parsing issue. */
RNA_define_verify_sdna(0);
prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "co", "totpoint");
RNA_def_property_struct_type(prop, "HairPoint");
RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size");
RNA_def_property_struct_type(prop, "CurvePoint");
RNA_def_property_ui_text(prop, "Points", "Control points of all hair curves");
RNA_define_verify_sdna(1);
/* Direct access to built-in attributes. */
RNA_define_verify_sdna(0);
prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_size");
RNA_def_property_struct_type(prop, "FloatVectorAttributeValue");
RNA_def_property_update(prop, 0, "rna_Hair_update_data");
RNA_define_verify_sdna(1);
prop = RNA_def_property(srna, "curve_offset_data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", NULL);
RNA_def_property_struct_type(prop, "IntAttributeValue");
RNA_def_property_collection_funcs(prop,
"rna_Hair_curve_offset_data_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_Hair_curve_offset_data_length",
NULL,
NULL,
NULL);
RNA_def_property_update(prop, 0, "rna_Hair_update_data");
/* materials */
prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");