diff --git a/intern/cycles/blender/curves.cpp b/intern/cycles/blender/curves.cpp index 65a02d041cc..4206a1d8a8b 100644 --- a/intern/cycles/blender/curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #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 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 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 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 b_attr_radius = find_curves_radius_attribute(b_hair); + /* Export curves and points. */ vector 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 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; diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index ee8ef5e97f7..73e00398084 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -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: diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index e4c18325d76..d235e6d15e1 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -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", diff --git a/source/blender/blenkernel/intern/hair.cc b/source/blender/blenkernel/intern/hair.cc index 5e8b81c03a4..bddadc3bcfd 100644 --- a/source/blender/blenkernel/intern/hair.cc +++ b/source/blender/blenkernel/intern/hair.cc @@ -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(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(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 offsets{geometry.offsets, geometry.curve_size + 1}; + MutableSpan positions{(float3 *)geometry.position, geometry.point_size}; + MutableSpan 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 curve_positions = positions.slice(curve_range); + MutableSpan 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(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); } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 888bd244007..ef146606ff0 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -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); diff --git a/source/blender/draw/intern/draw_cache_impl_hair.cc b/source/blender/draw/intern/draw_cache_impl_hair.cc index d236f0c2f59..cdc06b121b0 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.cc +++ b/source/blender/draw/intern/draw_cache_impl_hair.cc @@ -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 offsets{hair->geometry.offsets, hair->geometry.curve_size + 1}; + + Span 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 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 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++); } diff --git a/source/blender/makesdna/DNA_hair_types.h b/source/blender/makesdna/DNA_hair_types.h index 2e819b32033..5d54a4bb8cc 100644 --- a/source/blender/makesdna/DNA_hair_types.h +++ b/source/blender/makesdna/DNA_hair_types.h @@ -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; diff --git a/source/blender/makesrna/intern/rna_hair.c b/source/blender/makesrna/intern/rna_hair.c index 4ca66c6b583..30e1fd48c03 100644 --- a/source/blender/makesrna/intern/rna_hair.c +++ b/source/blender/makesrna/intern/rna_hair.c @@ -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");