From 14fb9edc419aadb39007d62e4d741bd855d153cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Thu, 30 Nov 2023 19:04:04 +0100 Subject: [PATCH 1/7] Alembic/USD: use geometry sets to import data This rewrites the Alembic and USD data importers to work with and output GeometrySets instead of Meshes. The main motivation for this change is to be able to import properly point clouds, which are currently imported as Meshes, and curves data, which suffer from a lot of issues due to limitations of legacy curves structures (fixed by the new curves data-block) and are also converted to Meshes. Further, for Curves, it will allow importing arbitrary attributes. This patch was primarily meant for Alembic, but changes to USD import were necessary as they share the same modifier. For Alembic: - there should be no behavioral changes for Meshes - points are imported as PointClouds - curves are imported as the new Curves object type which fixes an important bug (see note at the end) For USD: - there should be no behavioral changes for Meshes - curves are imported as the new Curves object type Note that the current USD importer does not support loading PointClouds, so this patch does not add support for it. For both Alembic and USD, knots arrays are not read anymore, as the new Curves object does not expose the ability to set them. As knots arrays from other software were always a little problematic to interpret, this might not really change anything for users. This fixes #58704: Animated Alembic curves don't update on render (NOTE: it could be that the fixed bug was also present in USD.) --- source/blender/blenkernel/intern/object.cc | 2 +- source/blender/io/alembic/ABC_alembic.h | 18 +- .../io/alembic/intern/abc_reader_curves.cc | 546 ++++++++++-------- .../io/alembic/intern/abc_reader_curves.h | 28 +- .../io/alembic/intern/abc_reader_mesh.cc | 37 ++ .../io/alembic/intern/abc_reader_mesh.h | 25 +- .../io/alembic/intern/abc_reader_object.cc | 13 +- .../io/alembic/intern/abc_reader_object.h | 17 +- .../io/alembic/intern/abc_reader_points.cc | 126 ++-- .../io/alembic/intern/abc_reader_points.h | 16 +- .../blender/io/alembic/intern/alembic_capi.cc | 24 +- .../blender/io/usd/intern/usd_capi_import.cc | 14 +- .../blender/io/usd/intern/usd_reader_curve.cc | 241 ++++---- .../blender/io/usd/intern/usd_reader_curve.h | 12 +- .../blender/io/usd/intern/usd_reader_geom.h | 10 +- .../blender/io/usd/intern/usd_reader_mesh.cc | 13 + .../blender/io/usd/intern/usd_reader_mesh.h | 10 +- .../blender/io/usd/intern/usd_reader_nurbs.cc | 10 + .../blender/io/usd/intern/usd_reader_nurbs.h | 9 +- .../blender/io/usd/intern/usd_reader_shape.h | 9 +- source/blender/io/usd/usd.h | 18 +- .../modifiers/intern/MOD_meshsequencecache.cc | 129 +++-- 22 files changed, 777 insertions(+), 550 deletions(-) diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 668494547a5..af2780035b3 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -1248,7 +1248,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) } if (ELEM(ob->type, OB_POINTCLOUD, OB_CURVES)) { - return modifier_type == eModifierType_Nodes; + return ELEM(modifier_type, eModifierType_Nodes, eModifierType_MeshSequenceCache); } if (ob->type == OB_VOLUME) { return mti->modify_geometry_set != nullptr; diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index 6081a965a01..4bd590b3578 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -124,12 +124,18 @@ typedef struct ABCReadParams { float velocity_scale; } ABCReadParams; -/* Either modifies existing_mesh in-place or constructs a new mesh. */ -struct Mesh *ABC_read_mesh(struct CacheReader *reader, - struct Object *ob, - struct Mesh *existing_mesh, - const ABCReadParams *params, - const char **err_str); +#ifdef __cplusplus +namespace blender::bke { +struct GeometrySet; +} + +/* Either modifies the existing geometry component, or create a new one. */ +void ABC_read_geometry(CacheReader *reader, + Object *ob, + blender::bke::GeometrySet &geometry_set, + const ABCReadParams *params, + const char **err_str); +#endif bool ABC_mesh_topology_changed(struct CacheReader *reader, struct Object *ob, diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index fd8dbbb422e..45b288d2924 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -15,17 +15,21 @@ #include "MEM_guardedalloc.h" -#include "DNA_curve_types.h" +#include "DNA_curves_types.h" #include "DNA_object_types.h" #include "BLI_listbase.h" +#include "BKE_curve.hh" +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BLT_translation.h" -#include "BKE_curve.hh" #include "BKE_mesh.hh" #include "BKE_object.hh" +#include "BLT_translation.h" + using Alembic::Abc::FloatArraySamplePtr; using Alembic::Abc::Int32ArraySamplePtr; using Alembic::Abc::P3fArraySamplePtr; @@ -43,6 +47,239 @@ using Alembic::AbcGeom::kWrapExisting; namespace blender::io::alembic { +static int16_t get_curve_resolution(const ICurvesSchema &schema, + const Alembic::Abc::ISampleSelector &sample_sel) +{ + ICompoundProperty user_props = schema.getUserProperties(); + if (!user_props) { + return 1; + } + + const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME); + if (!header || !header->isScalar() || !IInt16Property::matches(*header)) { + return 1; + } + + IInt16Property resolu(user_props, header->getName()); + return resolu.getValue(sample_sel); +} + +static int16_t get_curve_order(const Alembic::AbcGeom::CurveType abc_curve_type, + const UcharArraySamplePtr orders, + const size_t curve_index) +{ + switch (abc_curve_type) { + case Alembic::AbcGeom::kCubic: + return 4; + case Alembic::AbcGeom::kVariableOrder: + if (orders && orders->size() > curve_index) { + return int16_t((*orders)[curve_index]); + } + ATTR_FALLTHROUGH; + case Alembic::AbcGeom::kLinear: + default: + return 2; + } +} + +static int get_curve_overlap(const Alembic::AbcGeom::CurvePeriodicity periodicity, + const P3fArraySamplePtr positions, + const int idx, + const int num_verts, + const int16_t order) +{ + if (periodicity != Alembic::AbcGeom::kPeriodic) { + /* kNonPeriodic is always assumed to have no overlap. */ + return 0; + } + + /* Check the number of points which overlap, we don't have overlapping points in Blender, but + * other software do use them to indicate that a curve is actually cyclic. Usually the number of + * overlapping points is equal to the order/degree of the curve. + */ + + const int start = idx; + const int end = idx + num_verts; + int overlap = 0; + + for (int j = start, k = end - order; j < order; j++, k++) { + const Imath::V3f &p1 = (*positions)[j]; + const Imath::V3f &p2 = (*positions)[k]; + + if (p1 != p2) { + break; + } + + overlap++; + } + + /* TODO: Special case, need to figure out how it coincides with knots. */ + if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) { + overlap = 1; + } + + /* There is no real cycles. */ + return overlap; +} + +static CurveType get_curve_type(const Alembic::AbcGeom::BasisType basis) +{ + switch (basis) { + case Alembic::AbcGeom::kNoBasis: + return CURVE_TYPE_POLY; + case Alembic::AbcGeom::kBezierBasis: + return CURVE_TYPE_BEZIER; + case Alembic::AbcGeom::kBsplineBasis: + return CURVE_TYPE_NURBS; + case Alembic::AbcGeom::kCatmullromBasis: + return CURVE_TYPE_CATMULL_ROM; + case Alembic::AbcGeom::kHermiteBasis: + case Alembic::AbcGeom::kPowerBasis: + /* Those types are unknown to Blender, use a default poly type. */ + return CURVE_TYPE_POLY; + } + return CURVE_TYPE_POLY; +} + +static bool curves_topology_changed(const bke::CurvesGeometry &geometry, + Span preprocessed_offsets) +{ + /* Offsets have an extra element. */ + if (geometry.curve_num != preprocessed_offsets.size() - 1) { + return true; + } + + const Span offsets = geometry.offsets(); + for (const int i_curve : preprocessed_offsets.index_range()) { + if (offsets[i_curve] != preprocessed_offsets[i_curve]) { + return true; + } + } + + return false; +} + +/* Preprocessed data to help and simplify converting curve data from Alembic to Blender. + * As some operations may require to look up the Alembic sample multiple times, we just + * do it once and cache the results in this. + */ +struct PreprocessedSampleData { + /* This holds one value for each spline. This will be used to lookup the data at the right + * indices, and will also be used to set #CurveGeometry.offsets. */ + Vector offset_in_blender; + /* This holds one value for each spline, and tells where in the Alembic curve sample the spline + * actually starts, accounting for duplicate points indicating cyclicity. */ + Vector offset_in_alembic; + /* This holds one value for each spline to tell whether it is cyclic. */ + Vector curves_overlaps; + /* This holds one value for each spline which define its order. */ + Vector curves_orders; + + /* True if any values of `curves_overlaps` is true. If so, we will need to copy the + * `curves_overlaps` to an attribute on the Blender curves. */ + bool do_cyclic = false; + + /* Only one curve type for the whole objects. */ + CurveType curve_type; + + /* Store the pointers during preprocess so we do not have to look up the sample twice. */ + P3fArraySamplePtr positions = nullptr; + FloatArraySamplePtr weights = nullptr; + FloatArraySamplePtr radii = nullptr; +}; + +/* Compute topological information about the curves. We do this step mainly to properly account + * for curves overlaps which imply different offsets between Blender and Alembic, but also to + * validate the data and cache some values. */ +static std::optional preprocess_sample(StringRefNull iobject_name, + const ICurvesSchema &schema, + const ISampleSelector sample_sel) +{ + + ICurvesSchema::Sample smp; + try { + smp = schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", + iobject_name.c_str(), + schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + return {}; + } + + /* Note: although Alembic can store knots, we do not read them as the functionality is not + * exposed by the Blender's Curves API yet. */ + const Int32ArraySamplePtr per_curve_vertices_count = smp.getCurvesNumVertices(); + const P3fArraySamplePtr positions = smp.getPositions(); + const FloatArraySamplePtr weights = smp.getPositionWeights(); + const CurvePeriodicity periodicity = smp.getWrap(); + const UcharArraySamplePtr orders = smp.getOrders(); + + const IFloatGeomParam widths_param = schema.getWidthsParam(); + FloatArraySamplePtr radii; + if (widths_param.valid()) { + IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel); + radii = wsample.getVals(); + } + + const int curve_count = per_curve_vertices_count->size(); + + PreprocessedSampleData data; + /* Add 1 as these store offsets with the actual value being `offset[i + 1] - offset[i]`. */ + data.offset_in_blender.resize(curve_count + 1); + data.offset_in_alembic.resize(curve_count + 1); + data.curves_overlaps.resize(curve_count); + data.curve_type = get_curve_type(smp.getBasis()); + + if (data.curve_type == CURVE_TYPE_NURBS) { + data.curves_orders.resize(curve_count); + } + + /* Compute topological information. */ + + int blender_offset = 0; + int alembic_offset = 0; + for (size_t i = 0; i < curve_count; i++) { + const int vertices_count = (*per_curve_vertices_count)[i]; + + const int curve_order = get_curve_order(smp.getType(), orders, i); + + /* Check if the curve is cyclic. */ + const int overlap = get_curve_overlap( + periodicity, positions, alembic_offset, vertices_count, curve_order); + + data.offset_in_blender[i] = blender_offset; + data.offset_in_alembic[i] = alembic_offset; + data.curves_overlaps[i] = overlap != 0; + + if (data.curve_type == CURVE_TYPE_NURBS) { + data.curves_orders[i] = curve_order; + } + + data.do_cyclic |= overlap != 0; + blender_offset += vertices_count - overlap; + alembic_offset += vertices_count; + } + data.offset_in_blender[curve_count] = blender_offset; + data.offset_in_alembic[curve_count] = alembic_offset; + + /* Store relevant pointers. */ + + data.positions = positions; + + if (weights && weights->size() > 1) { + data.weights = weights; + } + + if (radii && radii->size() > 1) { + data.radii = radii; + } + + return data; +} + AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings) : AbcObjectReader(object, settings) { @@ -64,13 +301,13 @@ bool AbcCurveReader::accepts_object_type( { if (!Alembic::AbcGeom::ICurves::matches(alembic_header)) { *err_str = TIP_( - "Object type mismatch, Alembic object path pointed to Curves when importing, but not any " - "more"); + "Object type mismatch, Alembic object path pointed to Curves when importing, but not " + "anymore."); return false; } - if (ob->type != OB_CURVES_LEGACY) { - *err_str = TIP_("Object type mismatch, Alembic object path points to Curves"); + if (ob->type != OB_CURVES) { + *err_str = TIP_("Object type mismatch, Alembic object path points to Curves."); return false; } @@ -79,262 +316,101 @@ bool AbcCurveReader::accepts_object_type( void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) { - Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVES_LEGACY); + Curves *curves = static_cast(BKE_curves_add(bmain, m_data_name.c_str())); - cu->flag |= CU_3D; - cu->actvert = CU_ACT_NONE; - cu->resolu = 1; + m_object = BKE_object_add_only_object(bmain, OB_CURVES, m_object_name.c_str()); + m_object->data = curves; - ICompoundProperty user_props = m_curves_schema.getUserProperties(); - if (user_props) { - const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME); - if (header != nullptr && header->isScalar() && IInt16Property::matches(*header)) { - IInt16Property resolu(user_props, header->getName()); - cu->resolu = resolu.getValue(sample_sel); - } - } - - m_object = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, m_object_name.c_str()); - m_object->data = cu; - - read_curve_sample(cu, m_curves_schema, sample_sel); + read_curves_sample(curves, m_curves_schema, sample_sel); if (m_settings->always_add_cache_reader || has_animations(m_curves_schema, m_settings)) { addCacheModifier(); } } -void AbcCurveReader::read_curve_sample(Curve *cu, - const ICurvesSchema &schema, - const ISampleSelector &sample_sel) +void AbcCurveReader::read_curves_sample(Curves *curves, + const ICurvesSchema &schema, + const ISampleSelector &sample_sel) { - ICurvesSchema::Sample smp; - try { - smp = schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); + std::optional opt_preprocess = preprocess_sample( + m_iobject.getFullName(), schema, sample_sel); + + if (!opt_preprocess) { return; } - const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices(); - const P3fArraySamplePtr positions = smp.getPositions(); - const FloatArraySamplePtr weights = smp.getPositionWeights(); - const FloatArraySamplePtr knots = smp.getKnots(); - const CurvePeriodicity periodicity = smp.getWrap(); - const UcharArraySamplePtr orders = smp.getOrders(); + const PreprocessedSampleData &data = opt_preprocess.value(); - const IFloatGeomParam widths_param = schema.getWidthsParam(); - FloatArraySamplePtr radiuses; + const int point_count = data.offset_in_blender.last(); + const int curve_count = data.offset_in_blender.size() - 1; - if (widths_param.valid()) { - IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel); - radiuses = wsample.getVals(); + bke::CurvesGeometry &geometry = curves->geometry.wrap(); + + if (curves_topology_changed(geometry, data.offset_in_blender)) { + geometry.resize(point_count, curve_count); + geometry.offsets_for_write().copy_from(data.offset_in_blender); } - int knot_offset = 0; + geometry.fill_curve_types(data.curve_type); - size_t idx = 0; - for (size_t i = 0; i < num_vertices->size(); i++) { - const int num_verts = (*num_vertices)[i]; + if (data.curve_type != CURVE_TYPE_POLY) { + geometry.resolution_for_write().fill(get_curve_resolution(schema, sample_sel)); + } - Nurb *nu = static_cast(MEM_callocN(sizeof(Nurb), "abc_getnurb")); - nu->resolu = cu->resolu; - nu->resolv = cu->resolv; - nu->pntsu = num_verts; - nu->pntsv = 1; - nu->flag |= CU_SMOOTH; + MutableSpan curves_positions = geometry.positions_for_write(); + for (const int i_curve : geometry.curves_range()) { + int position_offset = data.offset_in_alembic[i_curve]; + for (const int i_point : geometry.points_by_curve()[i_curve]) { + const Imath::V3f &pos = (*data.positions)[position_offset++]; + copy_zup_from_yup(curves_positions[i_point], pos.getValue()); + } + } - switch (smp.getType()) { - case Alembic::AbcGeom::kCubic: - nu->orderu = 4; - break; - case Alembic::AbcGeom::kVariableOrder: - if (orders && orders->size() > i) { - nu->orderu = short((*orders)[i]); - break; - } - ATTR_FALLTHROUGH; - case Alembic::AbcGeom::kLinear: - default: - nu->orderu = 2; + if (data.do_cyclic) { + geometry.cyclic_for_write().copy_from(data.curves_overlaps); + } + + if (data.radii) { + bke::SpanAttributeWriter radii = + geometry.attributes_for_write().lookup_or_add_for_write_span("radius", + ATTR_DOMAIN_POINT); + + for (const int i_curve : geometry.curves_range()) { + int position_offset = data.offset_in_alembic[i_curve]; + for (const int i_point : geometry.points_by_curve()[i_curve]) { + radii.span[i_point] = (*data.radii)[position_offset++]; + } } - if (periodicity == Alembic::AbcGeom::kNonPeriodic) { - nu->flagu |= CU_NURB_ENDPOINT; + radii.finish(); + } + + if (data.curve_type == CURVE_TYPE_NURBS) { + geometry.nurbs_orders_for_write().copy_from(data.curves_orders); + + if (data.weights) { + MutableSpan curves_weights = geometry.nurbs_weights_for_write(); + Span data_weights_span = {data.weights->get(), int64_t(data.weights->size())}; + for (const int i_curve : geometry.curves_range()) { + const int alembic_offset = data.offset_in_alembic[i_curve]; + const IndexRange points = geometry.points_by_curve()[i_curve]; + curves_weights.slice(points).copy_from( + data_weights_span.slice(alembic_offset, points.size())); + } } - else if (periodicity == Alembic::AbcGeom::kPeriodic) { - nu->flagu |= CU_NURB_CYCLIC; - - /* Check the number of points which overlap, we don't have - * overlapping points in Blender, but other software do use them to - * indicate that a curve is actually cyclic. Usually the number of - * overlapping points is equal to the order/degree of the curve. - */ - - const int start = idx; - const int end = idx + num_verts; - int overlap = 0; - - for (int j = start, k = end - nu->orderu; j < nu->orderu; j++, k++) { - const Imath::V3f &p1 = (*positions)[j]; - const Imath::V3f &p2 = (*positions)[k]; - - if (p1 != p2) { - break; - } - - overlap++; - } - - /* TODO: Special case, need to figure out how it coincides with knots. */ - if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) { - overlap = 1; - } - - /* There is no real cycles. */ - if (overlap == 0) { - nu->flagu &= ~CU_NURB_CYCLIC; - nu->flagu |= CU_NURB_ENDPOINT; - } - - nu->pntsu -= overlap; - } - - const bool do_weights = (weights != nullptr) && (weights->size() > 1); - float weight = 1.0f; - - const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1); - float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f; - - nu->type = CU_NURBS; - - nu->bp = static_cast(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb")); - BPoint *bp = nu->bp; - - for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { - const Imath::V3f &pos = (*positions)[idx]; - - if (do_radius) { - radius = (*radiuses)[idx]; - } - - if (do_weights) { - weight = (*weights)[idx]; - } - - copy_zup_from_yup(bp->vec, pos.getValue()); - bp->vec[3] = weight; - bp->f1 = SELECT; - bp->radius = radius; - bp->weight = 1.0f; - } - - if (knots && knots->size() != 0) { - nu->knotsu = static_cast( - MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu")); - - /* TODO: second check is temporary, for until the check for cycles is rock solid. */ - if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) { - /* Skip first and last knots. */ - for (size_t i = 1; i < knots->size() - 1; i++) { - nu->knotsu[i - 1] = (*knots)[knot_offset + i]; - } - } - else { - /* TODO: figure out how to use the knots array from other - * software in this case. */ - BKE_nurb_knot_calc_u(nu); - } - - knot_offset += knots->size(); - } - else { - BKE_nurb_knot_calc_u(nu); - } - - BLI_addtail(BKE_curve_nurbs_get(cu), nu); } } -Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh, - const ISampleSelector &sample_sel, - int /*read_flag*/, - const char * /*velocity_name*/, - const float /*velocity_scale*/, - const char **err_str) +void AbcCurveReader::read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + int /*read_flag*/, + const char * /*velocity_name*/, + const float /*velocity_scale*/, + const char ** /*err_str*/) { - ICurvesSchema::Sample sample; + Curves *curves = geometry_set.get_curves_for_write(); - try { - sample = m_curves_schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - *err_str = TIP_("Error reading curve sample; more detail on the console"); - printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - m_curves_schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - return existing_mesh; - } - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices(); - - int vertex_idx = 0; - int curve_idx; - Curve *curve = static_cast(m_object->data); - - const int curve_count = BLI_listbase_count(&curve->nurb); - bool same_topology = curve_count == num_vertices->size(); - - if (same_topology) { - Nurb *nurbs = static_cast(curve->nurb.first); - for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { - const int num_in_alembic = (*num_vertices)[curve_idx]; - const int num_in_blender = nurbs->pntsu; - - if (num_in_alembic != num_in_blender) { - same_topology = false; - break; - } - } - } - - if (!same_topology) { - BKE_nurbList_free(&curve->nurb); - read_curve_sample(curve, m_curves_schema, sample_sel); - } - else { - Nurb *nurbs = static_cast(curve->nurb.first); - for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { - const int totpoint = (*num_vertices)[curve_idx]; - - if (nurbs->bp) { - BPoint *point = nurbs->bp; - - for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { - const Imath::V3f &pos = (*positions)[vertex_idx]; - copy_zup_from_yup(point->vec, pos.getValue()); - } - } - else if (nurbs->bezt) { - BezTriple *bezier = nurbs->bezt; - - for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { - const Imath::V3f &pos = (*positions)[vertex_idx]; - copy_zup_from_yup(bezier->vec[1], pos.getValue()); - } - } - } - } - - return BKE_mesh_new_nomain_from_curve(m_object); + read_curves_sample(curves, m_curves_schema, sample_sel); } } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h index 726da43ec5b..79afc6448a6 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.h +++ b/source/blender/io/alembic/intern/abc_reader_curves.h @@ -10,7 +10,7 @@ #include "abc_reader_mesh.h" #include "abc_reader_object.h" -struct Curve; +struct Curves; #define ABC_CURVE_RESOLUTION_U_PROPNAME "blender:resolution" @@ -28,23 +28,17 @@ class AbcCurveReader final : public AbcObjectReader { const char **err_str) const override; void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; - /** - * \note Alembic only stores data about control points, but the Mesh - * passed from the cache modifier contains the #DispList, which has more data - * than the control points, so to avoid corrupting the #DispList we modify the - * object directly and create a new Mesh from that. Also we might need to - * create new or delete existing NURBS in the curve. - */ - struct Mesh *read_mesh(struct Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, - const char *velocity_name, - float velocity_scale, - const char **err_str) override; - void read_curve_sample(Curve *cu, - const Alembic::AbcGeom::ICurvesSchema &schema, - const Alembic::Abc::ISampleSelector &sample_selector); + void read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char *velocity_name, + float velocity_scale, + const char **err_str) override; + + void read_curves_sample(Curves *curves, + const Alembic::AbcGeom::ICurvesSchema &schema, + const Alembic::Abc::ISampleSelector &sample_selector); }; } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 18f01206fa7..1d50cbc0ff6 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -30,6 +30,7 @@ #include "BLT_translation.h" #include "BKE_attribute.hh" +#include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_main.hh" #include "BKE_material.h" @@ -745,6 +746,24 @@ bool AbcMeshReader::topology_changed(const Mesh *existing_mesh, const ISampleSel return false; } +void AbcMeshReader::read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + const int read_flag, + const char *velocity_name, + const float velocity_scale, + const char **err_str) +{ + Mesh *mesh = geometry_set.get_mesh_for_write(); + + if (mesh == nullptr) { + return; + } + + Mesh *new_mesh = read_mesh(mesh, sample_sel, read_flag, velocity_name, velocity_scale, err_str); + + geometry_set.replace_mesh(new_mesh); +} + Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, const ISampleSelector &sample_sel, const int read_flag, @@ -1157,4 +1176,22 @@ Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, return mesh_to_export; } +void AbcSubDReader::read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + const int read_flag, + const char *velocity_name, + const float velocity_scale, + const char **err_str) +{ + Mesh *mesh = geometry_set.get_mesh_for_write(); + + if (mesh == nullptr) { + return; + } + + Mesh *new_mesh = read_mesh(mesh, sample_sel, read_flag, velocity_name, velocity_scale, err_str); + + geometry_set.replace_mesh(new_mesh); +} + } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h index a0241ab5e63..b329b0af3b6 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.h +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -33,7 +33,15 @@ class AbcMeshReader final : public AbcObjectReader { int read_flag, const char *velocity_name, float velocity_scale, - const char **err_str) override; + const char **err_str); + + void read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char *velocity_name, + float velocity_scale, + const char **err_str) override; + bool topology_changed(const Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel) override; @@ -58,18 +66,27 @@ class AbcSubDReader final : public AbcObjectReader { const Object *const ob, const char **err_str) const override; void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; + + void read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char *velocity_name, + const float velocity_scale, + const char **err_str) override; + + private: struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, const char *velocity_name, - float velocity_scale, - const char **err_str) override; + const float velocity_scale, + const char **err_str); }; void read_mverts(Mesh &mesh, const Alembic::AbcGeom::P3fArraySamplePtr positions, const Alembic::AbcGeom::N3fArraySamplePtr normals); -CDStreamConfig get_config(struct Mesh *mesh); +CDStreamConfig get_config(struct Mesh *mesh, const std::string &iobject_full_name); } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc index dc78a458200..5f470e164dc 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.cc +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -141,14 +141,13 @@ Imath::M44d get_matrix(const IXformSchema &schema, const chrono_t time) return blend_matrices(s0.getMatrix(), s1.getMatrix(), interpolation_settings->weight); } -Mesh *AbcObjectReader::read_mesh(Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector & /*sample_sel*/, - int /*read_flag*/, - const char * /*velocity_name*/, - const float /*velocity_scale*/, - const char ** /*err_str*/) +void AbcObjectReader::read_geometry(bke::GeometrySet & /*geometry_set*/, + const Alembic::Abc::ISampleSelector & /*sample_sel*/, + int /*read_flag*/, + const char * /*velocity_name*/, + const float /*velocity_scale*/, + const char ** /*err_str*/) { - return existing_mesh; } bool AbcObjectReader::topology_changed(const Mesh * /*existing_mesh*/, diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h index 6daf7dd5c2d..d35b0295b5b 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.h +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -17,6 +17,10 @@ struct Main; struct Mesh; struct Object; +namespace blender::bke { +struct GeometrySet; +} + using Alembic::AbcCoreAbstract::chrono_t; namespace blender::io::alembic { @@ -139,12 +143,13 @@ class AbcObjectReader { virtual void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) = 0; - virtual struct Mesh *read_mesh(struct Mesh *mesh, - const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, - const char *velocity_name, - float velocity_scale, - const char **err_str); + virtual void read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char *velocity_name, + float velocity_scale, + const char **err_str); + virtual bool topology_changed(const Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel); diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc index cce5c2740bf..7c74ca9913b 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.cc +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -7,6 +7,7 @@ */ #include "abc_reader_points.h" +#include "abc_axis_conversion.h" #include "abc_reader_mesh.h" #include "abc_reader_transform.h" #include "abc_util.h" @@ -14,22 +15,20 @@ #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" +#include "DNA_pointcloud_types.h" #include "BLT_translation.h" #include "BKE_customdata.hh" +#include "BKE_geometry_set.hh" +#include "BKE_lib_id.h" #include "BKE_mesh.hh" #include "BKE_object.hh" +#include "BKE_pointcloud.h" -using Alembic::AbcGeom::kWrapExisting; -using Alembic::AbcGeom::N3fArraySamplePtr; -using Alembic::AbcGeom::P3fArraySamplePtr; +#include "BLI_math_vector.h" -using Alembic::AbcGeom::ICompoundProperty; -using Alembic::AbcGeom::IN3fArrayProperty; -using Alembic::AbcGeom::IPoints; -using Alembic::AbcGeom::IPointsSchema; -using Alembic::AbcGeom::ISampleSelector; +using namespace Alembic::AbcGeom; namespace blender::io::alembic { @@ -58,8 +57,8 @@ bool AbcPointsReader::accepts_object_type( return false; } - if (ob->type != OB_MESH) { - *err_str = TIP_("Object type mismatch, Alembic object path points to Points"); + if (ob->type != OB_POINTCLOUD) { + *err_str = TIP_("Object type mismatch, Alembic object path points to Points."); return false; } @@ -68,28 +67,38 @@ bool AbcPointsReader::accepts_object_type( void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) { - Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, "", 0.0f, nullptr); + PointCloud *point_cloud = static_cast( + BKE_pointcloud_add_default(bmain, m_data_name.c_str())); - if (read_mesh != mesh) { - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object); + bke::GeometrySet geometry_set = bke::GeometrySet::from_pointcloud( + point_cloud, bke::GeometryOwnershipType::Editable); + read_geometry(geometry_set, sample_sel, 0, "", 1.0f, nullptr); + + PointCloud *read_point_cloud = + geometry_set.get_component_for_write().release(); + + if (read_point_cloud != point_cloud) { + BKE_pointcloud_nomain_to_pointcloud(read_point_cloud, point_cloud); } - if (m_settings->validate_meshes) { - BKE_mesh_validate(mesh, false, false); - } - - m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); - m_object->data = mesh; + m_object = BKE_object_add_only_object(bmain, OB_POINTCLOUD, m_object_name.c_str()); + m_object->data = point_cloud; if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) { addCacheModifier(); } } -void read_points_sample(const IPointsSchema &schema, - const ISampleSelector &selector, - CDStreamConfig &config) +static void read_points(const P3fArraySamplePtr positions, MutableSpan r_points) +{ + for (size_t i = 0; i < positions->size(); i++) { + copy_zup_from_yup(r_points[i], (*positions)[i].getValue()); + } +} + +static N3fArraySamplePtr read_points_sample(const IPointsSchema &schema, + const ISampleSelector &selector, + MutableSpan r_points) { Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector); @@ -108,16 +117,19 @@ void read_points_sample(const IPointsSchema &schema, } } - read_mverts(*config.mesh, positions, vnormals); + read_points(positions, r_points); + return vnormals; } -Mesh *AbcPointsReader::read_mesh(Mesh *existing_mesh, - const ISampleSelector &sample_sel, - int /*read_flag*/, - const char * /*velocity_name*/, - const float /*velocity_scale*/, - const char **err_str) +void AbcPointsReader::read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + int /*read_flag*/, + const char * /*velocity_name*/, + const float /*velocity_scale*/, + const char **err_str) { + BLI_assert(geometry_set.has_pointcloud()); + IPointsSchema::Sample sample; try { sample = m_schema.getValue(sample_sel); @@ -129,22 +141,60 @@ Mesh *AbcPointsReader::read_mesh(Mesh *existing_mesh, m_schema.getName().c_str(), sample_sel.getRequestedTime(), ex.what()); - return existing_mesh; + return; } + PointCloud *existing_point_cloud = geometry_set.get_pointcloud_for_write(); + PointCloud *point_cloud = existing_point_cloud; + const P3fArraySamplePtr &positions = sample.getPositions(); - Mesh *new_mesh = nullptr; + const IFloatGeomParam widths_param = m_schema.getWidthsParam(); + FloatArraySamplePtr radii; - if (existing_mesh->totvert != positions->size()) { - new_mesh = BKE_mesh_new_nomain(positions->size(), 0, 0, 0); + if (widths_param.valid()) { + IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel); + radii = wsample.getVals(); } - Mesh *mesh_to_export = new_mesh ? new_mesh : existing_mesh; - CDStreamConfig config = get_config(mesh_to_export); - read_points_sample(m_schema, sample_sel, config); + if (point_cloud->totpoint != positions->size()) { + point_cloud = BKE_pointcloud_new_nomain(positions->size()); + } - return mesh_to_export; + bke::MutableAttributeAccessor attribute_accessor = point_cloud->attributes_for_write(); + + bke::SpanAttributeWriter positions_writer = + attribute_accessor.lookup_or_add_for_write_span("position", ATTR_DOMAIN_POINT); + MutableSpan point_positions = positions_writer.span; + N3fArraySamplePtr normals = read_points_sample(m_schema, sample_sel, point_positions); + positions_writer.finish(); + + bke::SpanAttributeWriter point_radii_writer = + attribute_accessor.lookup_or_add_for_write_span("radius", ATTR_DOMAIN_POINT); + MutableSpan point_radii = point_radii_writer.span; + + if (radii) { + for (size_t i = 0; i < radii->size(); i++) { + point_radii[i] = (*radii)[i]; + } + } + else { + point_radii.fill(0.01f); + } + point_radii_writer.finish(); + + if (normals) { + bke::SpanAttributeWriter normals_writer = + attribute_accessor.lookup_or_add_for_write_span("N", ATTR_DOMAIN_POINT); + MutableSpan point_normals = normals_writer.span; + for (size_t i = 0; i < normals->size(); i++) { + Imath::V3f nor_in = (*normals)[i]; + copy_zup_from_yup(point_normals[i], nor_in.getValue()); + } + normals_writer.finish(); + } + + geometry_set.replace_pointcloud(point_cloud); } } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h index 2a843fc3b96..dd918b8ea63 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.h +++ b/source/blender/io/alembic/intern/abc_reader_points.h @@ -26,16 +26,12 @@ class AbcPointsReader final : public AbcObjectReader { void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; - struct Mesh *read_mesh(struct Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, - const char *velocity_name, - float velocity_scale, - const char **err_str) override; + void read_geometry(bke::GeometrySet &geometry_set, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char *velocity_name, + float velocity_scale, + const char **err_str) override; }; -void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, - const Alembic::AbcGeom::ISampleSelector &selector, - CDStreamConfig &config); - } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index db9d1c34ea9..18a7d692473 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -792,24 +792,24 @@ static ISampleSelector sample_selector_for_time(chrono_t time) return ISampleSelector(time, ISampleSelector::kFloorIndex); } -Mesh *ABC_read_mesh(CacheReader *reader, - Object *ob, - Mesh *existing_mesh, - const ABCReadParams *params, - const char **err_str) +void ABC_read_geometry(CacheReader *reader, + Object *ob, + blender::bke::GeometrySet &geometry_set, + const ABCReadParams *params, + const char **err_str) { AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); if (abc_reader == nullptr) { - return nullptr; + return; } ISampleSelector sample_sel = sample_selector_for_time(params->time); - return abc_reader->read_mesh(existing_mesh, - sample_sel, - params->read_flags, - params->velocity_name, - params->velocity_scale, - err_str); + return abc_reader->read_geometry(geometry_set, + sample_sel, + params->read_flags, + params->velocity_name, + params->velocity_scale, + err_str); } bool ABC_mesh_topology_changed(CacheReader *reader, diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index d9b6865a81e..5d610167774 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -557,19 +557,19 @@ USDMeshReadParams create_mesh_read_params(const double motion_sample_time, const return params; } -Mesh *USD_read_mesh(CacheReader *reader, - Object *ob, - Mesh *existing_mesh, - const USDMeshReadParams params, - const char **err_str) +void USD_read_geometry(CacheReader *reader, + Object *ob, + blender::bke::GeometrySet &geometry_set, + const USDMeshReadParams params, + const char **err_str) { USDGeomReader *usd_reader = dynamic_cast(get_usd_reader(reader, ob, err_str)); if (usd_reader == nullptr) { - return nullptr; + return; } - return usd_reader->read_mesh(existing_mesh, params, err_str); + return usd_reader->read_geometry(geometry_set, params, err_str); } bool USD_mesh_topology_changed(CacheReader *reader, diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc index 6ae84e6a21d..e6c51b16c37 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.cc +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -6,13 +6,14 @@ #include "usd_reader_curve.h" -#include "BKE_curve.hh" +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_mesh.hh" #include "BKE_object.hh" #include "BLI_listbase.h" -#include "DNA_curve_types.h" +#include "DNA_curves_types.h" #include "DNA_object_types.h" #include "MEM_guardedalloc.h" @@ -26,21 +27,59 @@ namespace blender::io::usd { +/** Return the sum of the values of each element in `usdCounts`. This is used for precomputing the + * total number of points for all curves in some curve primitive. */ +static int accumulate_point_count(const pxr::VtIntArray &usdCounts) +{ + int result = 0; + for (int v : usdCounts) { + result += v; + } + return result; +} + +/** Returns true if the number of curves or the number of curve points in each curve differ. */ +static bool curves_topology_changed(const CurvesGeometry &geometry, + const pxr::VtIntArray &usdCounts) +{ + if (geometry.curve_num != usdCounts.size()) { + return true; + } + + for (int curve_idx = 0; curve_idx < geometry.curve_num; curve_idx++) { + const int num_in_usd = usdCounts[curve_idx]; + const int num_in_blender = geometry.curve_offsets[curve_idx]; + + if (num_in_usd != num_in_blender) { + return true; + } + } + + return false; +} + +static CurveType get_curve_type(pxr::TfToken basis) +{ + if (basis == pxr::UsdGeomTokens->bspline) { + return CURVE_TYPE_NURBS; + } + if (basis == pxr::UsdGeomTokens->bezier) { + return CURVE_TYPE_BEZIER; + } + return CURVE_TYPE_POLY; +} + void USDCurvesReader::create_object(Main *bmain, const double /*motionSampleTime*/) { - curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVES_LEGACY); + curve_ = static_cast(BKE_curves_add(bmain, name_.c_str())); - curve_->flag |= CU_3D; - curve_->actvert = CU_ACT_NONE; - curve_->resolu = 2; - - object_ = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, name_.c_str()); + object_ = BKE_object_add_only_object(bmain, OB_CURVES, name_.c_str()); object_->data = curve_; } void USDCurvesReader::read_object_data(Main *bmain, double motionSampleTime) { - Curve *cu = (Curve *)object_->data; + Curves *cu = (Curves *)object_->data; read_curve_sample(cu, motionSampleTime); if (curve_prim_.GetPointsAttr().ValueMightBeTimeVarying()) { @@ -50,7 +89,7 @@ void USDCurvesReader::read_object_data(Main *bmain, double motionSampleTime) USDXformReader::read_object_data(bmain, motionSampleTime); } -void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime) +void USDCurvesReader::read_curve_sample(Curves *cu, const double motionSampleTime) { curve_prim_ = pxr::UsdGeomBasisCurves(prim_); @@ -65,7 +104,6 @@ void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime pxr::VtIntArray usdCounts; vertexAttr.Get(&usdCounts, motionSampleTime); - int num_subcurves = usdCounts.size(); pxr::VtVec3fArray usdPoints; pointsAttr.Get(&usdPoints, motionSampleTime); @@ -85,155 +123,70 @@ void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime pxr::TfToken wrap; wrapAttr.Get(&wrap, motionSampleTime); - pxr::VtVec3fArray usdNormals; - curve_prim_.GetNormalsAttr().Get(&usdNormals, motionSampleTime); + bke::CurvesGeometry &geometry = cu->geometry.wrap(); + const int num_subcurves = usdCounts.size(); + const int num_points = accumulate_point_count(usdCounts); - /* If normals, extrude, else bevel. - * Perhaps to be replaced by Blender/USD Schema. */ - if (!usdNormals.empty()) { - /* Set extrusion to 1.0f. */ - curve_->extrude = 1.0f; - } - else { - /* Set bevel depth to 1.0f. */ - curve_->bevel_radius = 1.0f; + if (curves_topology_changed(geometry, usdCounts)) { + geometry.resize(num_points, num_subcurves); } - size_t idx = 0; + MutableSpan offsets = geometry.offsets_for_write(); + MutableSpan positions = geometry.positions_for_write(); + + const CurveType curve_type = get_curve_type(basis); + geometry.fill_curve_types(curve_type); + + if (wrap == pxr::UsdGeomTokens->periodic) { + geometry.cyclic_for_write().fill(true); + } + + if (curve_type == CURVE_TYPE_NURBS) { + const int8_t curve_order = type == pxr::UsdGeomTokens->cubic ? 4 : 2; + geometry.nurbs_orders_for_write().fill(curve_order); + } + + const int default_resolution = 2; + geometry.resolution_for_write().fill(default_resolution); + + int offset = 0; for (size_t i = 0; i < num_subcurves; i++) { const int num_verts = usdCounts[i]; - Nurb *nu = static_cast(MEM_callocN(sizeof(Nurb), __func__)); + offsets[i] = offset; + offset += num_verts; + } - if (basis == pxr::UsdGeomTokens->bspline) { - nu->flag = CU_SMOOTH; - nu->type = CU_NURBS; + for (const int i_point : geometry.points_range()) { + positions[i_point][0] = float(usdPoints[i_point][0]); + positions[i_point][1] = float(usdPoints[i_point][1]); + positions[i_point][2] = float(usdPoints[i_point][2]); + } + + if (usdWidths.size()) { + bke::SpanAttributeWriter radii = + geometry.attributes_for_write().lookup_or_add_for_write_span("radius", + ATTR_DOMAIN_POINT); + for (const int i_point : geometry.points_range()) { + radii.span[i_point] = usdWidths[i_point]; } - else if (basis == pxr::UsdGeomTokens->bezier) { - /* TODO(makowalski): Beziers are not properly imported as beziers. */ - nu->type = CU_POLY; - } - else if (basis.IsEmpty()) { - nu->type = CU_POLY; - } - nu->resolu = cu->resolu; - nu->resolv = cu->resolv; - - nu->pntsu = num_verts; - nu->pntsv = 1; - - if (type == pxr::UsdGeomTokens->cubic) { - nu->orderu = 4; - } - else if (type == pxr::UsdGeomTokens->linear) { - nu->orderu = 2; - } - - if (wrap == pxr::UsdGeomTokens->periodic) { - nu->flagu |= CU_NURB_CYCLIC; - } - else if (wrap == pxr::UsdGeomTokens->pinned) { - nu->flagu |= CU_NURB_ENDPOINT; - } - - float weight = 1.0f; - - nu->bp = static_cast(MEM_callocN(sizeof(BPoint) * nu->pntsu, __func__)); - BPoint *bp = nu->bp; - - for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { - bp->vec[0] = float(usdPoints[idx][0]); - bp->vec[1] = float(usdPoints[idx][1]); - bp->vec[2] = float(usdPoints[idx][2]); - bp->vec[3] = weight; - bp->f1 = SELECT; - bp->weight = weight; - - float radius = curve_->offset; - if (idx < usdWidths.size()) { - radius = usdWidths[idx]; - } - - bp->radius = radius; - } - - BKE_nurb_knot_calc_u(nu); - BKE_nurb_knot_calc_v(nu); - - BLI_addtail(BKE_curve_nurbs_get(cu), nu); + radii.finish(); } } -Mesh *USDCurvesReader::read_mesh(Mesh *existing_mesh, - const USDMeshReadParams params, - const char ** /*err_str*/) +void USDCurvesReader::read_geometry(bke::GeometrySet &geometry_set, + const USDMeshReadParams params, + const char ** /*err_str*/) { if (!curve_prim_) { - return existing_mesh; + return; } - pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr(); - pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr(); - pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); - - pxr::VtIntArray usdCounts; - - vertexAttr.Get(&usdCounts, params.motion_sample_time); - int num_subcurves = usdCounts.size(); - - pxr::VtVec3fArray usdPoints; - pointsAttr.Get(&usdPoints, params.motion_sample_time); - - int vertex_idx = 0; - int curve_idx; - Curve *curve = static_cast(object_->data); - - const int curve_count = BLI_listbase_count(&curve->nurb); - bool same_topology = curve_count == num_subcurves; - - if (same_topology) { - Nurb *nurbs = static_cast(curve->nurb.first); - for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { - const int num_in_usd = usdCounts[curve_idx]; - const int num_in_blender = nurbs->pntsu; - - if (num_in_usd != num_in_blender) { - same_topology = false; - break; - } - } + if (!geometry_set.has_curves()) { + return; } - if (!same_topology) { - BKE_nurbList_free(&curve->nurb); - read_curve_sample(curve, params.motion_sample_time); - } - else { - Nurb *nurbs = static_cast(curve->nurb.first); - for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { - const int totpoint = usdCounts[curve_idx]; - - if (nurbs->bp) { - BPoint *point = nurbs->bp; - - for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { - point->vec[0] = usdPoints[vertex_idx][0]; - point->vec[1] = usdPoints[vertex_idx][1]; - point->vec[2] = usdPoints[vertex_idx][2]; - } - } - else if (nurbs->bezt) { - BezTriple *bezier = nurbs->bezt; - - for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { - bezier->vec[1][0] = usdPoints[vertex_idx][0]; - bezier->vec[1][1] = usdPoints[vertex_idx][1]; - bezier->vec[1][2] = usdPoints[vertex_idx][2]; - } - } - } - } - - return BKE_mesh_new_nomain_from_curve(object_); + Curves *curves = geometry_set.get_curves_for_write(); + read_curve_sample(curves, params.motion_sample_time); } } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_curve.h b/source/blender/io/usd/intern/usd_reader_curve.h index 819caa61b39..08af8dd9697 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.h +++ b/source/blender/io/usd/intern/usd_reader_curve.h @@ -10,14 +10,14 @@ #include "pxr/usd/usdGeom/basisCurves.h" -struct Curve; +struct Curves; namespace blender::io::usd { class USDCurvesReader : public USDGeomReader { protected: pxr::UsdGeomBasisCurves curve_prim_; - Curve *curve_; + Curves *curve_; public: USDCurvesReader(const pxr::UsdPrim &prim, @@ -35,11 +35,11 @@ class USDCurvesReader : public USDGeomReader { void create_object(Main *bmain, double motionSampleTime) override; void read_object_data(Main *bmain, double motionSampleTime) override; - void read_curve_sample(Curve *cu, double motionSampleTime); + void read_curve_sample(Curves *cu, double motionSampleTime); - Mesh *read_mesh(struct Mesh *existing_mesh, - USDMeshReadParams params, - const char **err_str) override; + void read_geometry(bke::GeometrySet &geometry_set, + USDMeshReadParams params, + const char **err_str) override; }; } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_geom.h b/source/blender/io/usd/intern/usd_reader_geom.h index 8a7af0737fa..14bb9d96352 100644 --- a/source/blender/io/usd/intern/usd_reader_geom.h +++ b/source/blender/io/usd/intern/usd_reader_geom.h @@ -8,6 +8,10 @@ struct Mesh; +namespace blender::bke { +struct GeometrySet; +} + namespace blender::io::usd { class USDGeomReader : public USDXformReader { @@ -20,9 +24,9 @@ class USDGeomReader : public USDXformReader { { } - virtual Mesh *read_mesh(struct Mesh *existing_mesh, - USDMeshReadParams params, - const char **err_str) = 0; + virtual void read_geometry(bke::GeometrySet &geometry_set, + USDMeshReadParams params, + const char **err_str) = 0; virtual bool topology_changed(const Mesh * /*existing_mesh*/, double /*motionSampleTime*/) { diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 6b7b9fb7ab8..9242214c4d4 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -11,6 +11,7 @@ #include "BKE_attribute.hh" #include "BKE_customdata.hh" +#include "BKE_geometry_set.hh" #include "BKE_main.hh" #include "BKE_material.h" #include "BKE_mesh.hh" @@ -1137,6 +1138,18 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, return active_mesh; } +void USDMeshReader::read_geometry(bke::GeometrySet &geometry_set, + const USDMeshReadParams params, + const char **err_str) +{ + Mesh *existing_mesh = geometry_set.get_mesh_for_write(); + Mesh *new_mesh = read_mesh(existing_mesh, params, err_str); + + if (new_mesh != existing_mesh) { + geometry_set.replace_mesh(new_mesh); + } +} + std::string USDMeshReader::get_skeleton_path() const { /* Make sure we can apply UsdSkelBindingAPI to the prim. diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h index 90fde134fe6..81e2ae279e8 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.h +++ b/source/blender/io/usd/intern/usd_reader_mesh.h @@ -50,9 +50,9 @@ class USDMeshReader : public USDGeomReader { void create_object(Main *bmain, double motionSampleTime) override; void read_object_data(Main *bmain, double motionSampleTime) override; - struct Mesh *read_mesh(struct Mesh *existing_mesh, - USDMeshReadParams params, - const char **err_str) override; + void read_geometry(bke::GeometrySet &geometry_set, + USDMeshReadParams params, + const char **err_str) override; bool topology_changed(const Mesh *existing_mesh, double motionSampleTime) override; @@ -84,6 +84,10 @@ class USDMeshReader : public USDGeomReader { double motionSampleTime, bool new_mesh); + Mesh *read_mesh(struct Mesh *existing_mesh, + const USDMeshReadParams params, + const char **err_str); + void read_custom_data(const ImportSettings *settings, Mesh *mesh, double motionSampleTime, diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc index 16d4d8dbca0..b5f5d905f5f 100644 --- a/source/blender/io/usd/intern/usd_reader_nurbs.cc +++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc @@ -8,6 +8,7 @@ #include "usd_reader_nurbs.h" #include "BKE_curve.hh" +#include "BKE_geometry_set.hh" #include "BKE_mesh.hh" #include "BKE_object.hh" @@ -168,6 +169,15 @@ void USDNurbsReader::read_curve_sample(Curve *cu, const double motionSampleTime) } } +void USDNurbsReader::read_geometry(bke::GeometrySet &geometry_set, + const USDMeshReadParams params, + const char **err_str) +{ + BLI_assert(geometry_set.has_mesh()); + Mesh *new_mesh = read_mesh(nullptr, params, err_str); + geometry_set.replace_mesh(new_mesh); +} + Mesh *USDNurbsReader::read_mesh(Mesh * /*existing_mesh*/, const USDMeshReadParams params, const char ** /*err_str*/) diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.h b/source/blender/io/usd/intern/usd_reader_nurbs.h index 84fab3cabcf..fbf4d7c5579 100644 --- a/source/blender/io/usd/intern/usd_reader_nurbs.h +++ b/source/blender/io/usd/intern/usd_reader_nurbs.h @@ -39,9 +39,12 @@ class USDNurbsReader : public USDGeomReader { void read_curve_sample(Curve *cu, double motionSampleTime); - Mesh *read_mesh(struct Mesh *existing_mesh, - USDMeshReadParams params, - const char **err_str) override; + void read_geometry(bke::GeometrySet &geometry_set, + USDMeshReadParams params, + const char **err_str) override; + + private: + Mesh *read_mesh(struct Mesh *existing_mesh, USDMeshReadParams params, const char **err_str); }; } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_shape.h b/source/blender/io/usd/intern/usd_reader_shape.h index 1d31b51d90e..5aa69b7b423 100644 --- a/source/blender/io/usd/intern/usd_reader_shape.h +++ b/source/blender/io/usd/intern/usd_reader_shape.h @@ -48,9 +48,12 @@ class USDShapeReader : public USDGeomReader { void create_object(Main *bmain, double /*motionSampleTime*/) override; void read_object_data(Main *bmain, double motionSampleTime) override; - Mesh *read_mesh(Mesh *existing_mesh, - USDMeshReadParams params, - const char ** /*err_str*/) override; + void read_geometry(bke::GeometrySet & /*geometry_set*/, + USDMeshReadParams /*params*/, + const char ** /*err_str*/) override + { + } + Mesh *read_mesh(Mesh *existing_mesh, USDMeshReadParams params, const char ** /*err_str*/); bool is_time_varying(); virtual bool topology_changed(const Mesh * /*existing_mesh*/, diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 3b40d86cfb7..4bf70895cf0 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -14,6 +14,7 @@ extern "C" { struct CacheArchiveHandle; struct CacheReader; +struct Mesh; struct Object; struct bContext; struct wmJobWorkerStatus; @@ -145,12 +146,17 @@ void USD_free_handle(struct CacheArchiveHandle *handle); void USD_get_transform(struct CacheReader *reader, float r_mat[4][4], float time, float scale); -/* Either modifies current_mesh in-place or constructs a new mesh. */ -struct Mesh *USD_read_mesh(struct CacheReader *reader, - struct Object *ob, - struct Mesh *existing_mesh, - USDMeshReadParams params, - const char **err_str); +#ifdef __cplusplus +namespace blender::bke { +struct GeometrySet; +} + +void USD_read_geometry(CacheReader *reader, + Object *ob, + blender::bke::GeometrySet &geometry_set, + USDMeshReadParams params, + const char **err_str); +#endif bool USD_mesh_topology_changed(struct CacheReader *reader, const struct Object *ob, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 57074de7a41..46f982b9cfa 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -27,6 +27,7 @@ #include "BKE_cachefile.h" #include "BKE_context.hh" +#include "BKE_geometry_set.hh" #include "BKE_lib_query.h" #include "BKE_mesh.hh" #include "BKE_object.hh" @@ -62,8 +63,7 @@ # include "usd.h" #endif -using blender::float3; -using blender::Span; +using namespace blender; static void init_data(ModifierData *md) { @@ -168,6 +168,89 @@ static Mesh *generate_bounding_box_mesh(const Mesh *org_mesh) #endif +static void modify_geometry_set(ModifierData *md, + const ModifierEvalContext *ctx, + bke::GeometrySet *geometry_set) +{ +#if defined(WITH_USD) || defined(WITH_ALEMBIC) + MeshSeqCacheModifierData *mcmd = reinterpret_cast(md); + + Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + CacheFile *cache_file = mcmd->cache_file; + const float frame = DEG_get_ctime(ctx->depsgraph); + const float time = BKE_cachefile_time_offset(cache_file, frame, FPS); + const char *err_str = nullptr; + + if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) { + STRNCPY(mcmd->reader_object_path, mcmd->object_path); + BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path); + if (!mcmd->reader) { + BKE_modifier_set_error( + ctx->object, md, "Could not create cache reader for file %s", cache_file->filepath); + return; + } + } + + if (geometry_set->has_mesh()) { + const Mesh *mesh = geometry_set->get_mesh(); + if (can_use_mesh_for_orco_evaluation(mcmd, ctx, mesh, time, &err_str)) { + return; + } + } + + /* Do not process data if using a render procedural, return a box instead for displaying in the + * viewport. */ + if (BKE_cache_file_uses_render_procedural(cache_file, scene)) { + const Mesh *org_mesh = nullptr; + if (geometry_set->has_mesh()) { + org_mesh = geometry_set->get_mesh(); + } + + Mesh *bbox = generate_bounding_box_mesh(org_mesh); + *geometry_set = bke::GeometrySet::from_mesh(bbox, bke::GeometryOwnershipType::Editable); + return; + } + + /* Time (in frames or seconds) between two velocity samples. Automatically computed to + * scale the velocity vectors at render time for generating proper motion blur data. */ + float velocity_scale = mcmd->velocity_scale; + if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_FRAME) { + velocity_scale *= FPS; + } + + switch (cache_file->type) { + case CACHEFILE_TYPE_ALEMBIC: { +# ifdef WITH_ALEMBIC + ABCReadParams params; + params.time = time; + params.read_flags = mcmd->read_flag; + params.velocity_name = mcmd->cache_file->velocity_name; + params.velocity_scale = velocity_scale; + ABC_read_geometry(mcmd->reader, ctx->object, *geometry_set, ¶ms, &err_str); +# endif + break; + } + case CACHEFILE_TYPE_USD: { +# ifdef WITH_USD + const USDMeshReadParams params = create_mesh_read_params(time * FPS, mcmd->read_flag); + USD_read_geometry(mcmd->reader, ctx->object, *geometry_set, params, &err_str); +# endif + break; + } + case CACHE_FILE_TYPE_INVALID: + break; + } + + if (err_str) { + BKE_modifier_set_error(ctx->object, md, "%s", err_str); + } + +#else + UNUSED_VARS(ctx, md, geometry_set); + return; +#endif +} + static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { #if defined(WITH_USD) || defined(WITH_ALEMBIC) @@ -229,42 +312,10 @@ static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh } } - Mesh *result = nullptr; - - switch (cache_file->type) { - case CACHEFILE_TYPE_ALEMBIC: { -# ifdef WITH_ALEMBIC - /* Time (in frames or seconds) between two velocity samples. Automatically computed to - * scale the velocity vectors at render time for generating proper motion blur data. */ - float velocity_scale = mcmd->velocity_scale; - if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_FRAME) { - velocity_scale *= FPS; - } - - ABCReadParams params = {}; - params.time = time; - params.read_flags = mcmd->read_flag; - params.velocity_name = mcmd->cache_file->velocity_name; - params.velocity_scale = velocity_scale; - - result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, ¶ms, &err_str); -# endif - break; - } - case CACHEFILE_TYPE_USD: { -# ifdef WITH_USD - const USDMeshReadParams params = create_mesh_read_params(time * FPS, mcmd->read_flag); - result = USD_read_mesh(mcmd->reader, ctx->object, mesh, params, &err_str); -# endif - break; - } - case CACHE_FILE_TYPE_INVALID: - break; - } - - if (err_str) { - BKE_modifier_set_error(ctx->object, md, "%s", err_str); - } + bke::GeometrySet geometry_set = bke::GeometrySet::from_mesh( + mesh, bke::GeometryOwnershipType::Editable); + modify_geometry_set(md, ctx, &geometry_set); + Mesh *result = geometry_set.get_component_for_write().release(); if (!ELEM(result, nullptr, mesh) && (mesh != org_mesh)) { BKE_id_free(nullptr, mesh); @@ -446,7 +497,7 @@ ModifierTypeInfo modifierType_MeshSequenceCache = { /*deform_verts_EM*/ nullptr, /*deform_matrices_EM*/ nullptr, /*modify_mesh*/ modify_mesh, - /*modify_geometry_set*/ nullptr, + /*modify_geometry_set*/ modify_geometry_set, /*init_data*/ init_data, /*required_data_mask*/ nullptr, -- 2.30.2 From 300a3a61982a87c51eeb9b496d5987654b213d8e Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Wed, 10 Jan 2024 00:40:03 -0800 Subject: [PATCH 2/7] ATTR_DOMAIN_POINT to bke::AttrDomain::Point --- source/blender/io/alembic/intern/abc_reader_curves.cc | 4 ++-- source/blender/io/alembic/intern/abc_reader_points.cc | 6 +++--- source/blender/io/usd/intern/usd_reader_curve.cc | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index 45b288d2924..0e4098db1b5 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -372,8 +372,8 @@ void AbcCurveReader::read_curves_sample(Curves *curves, if (data.radii) { bke::SpanAttributeWriter radii = - geometry.attributes_for_write().lookup_or_add_for_write_span("radius", - ATTR_DOMAIN_POINT); + geometry.attributes_for_write().lookup_or_add_for_write_span( + "radius", bke::AttrDomain::Point); for (const int i_curve : geometry.curves_range()) { int position_offset = data.offset_in_alembic[i_curve]; diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc index 7c74ca9913b..7395eb541cf 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.cc +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -164,13 +164,13 @@ void AbcPointsReader::read_geometry(bke::GeometrySet &geometry_set, bke::MutableAttributeAccessor attribute_accessor = point_cloud->attributes_for_write(); bke::SpanAttributeWriter positions_writer = - attribute_accessor.lookup_or_add_for_write_span("position", ATTR_DOMAIN_POINT); + attribute_accessor.lookup_or_add_for_write_span("position", bke::AttrDomain::Point); MutableSpan point_positions = positions_writer.span; N3fArraySamplePtr normals = read_points_sample(m_schema, sample_sel, point_positions); positions_writer.finish(); bke::SpanAttributeWriter point_radii_writer = - attribute_accessor.lookup_or_add_for_write_span("radius", ATTR_DOMAIN_POINT); + attribute_accessor.lookup_or_add_for_write_span("radius", bke::AttrDomain::Point); MutableSpan point_radii = point_radii_writer.span; if (radii) { @@ -185,7 +185,7 @@ void AbcPointsReader::read_geometry(bke::GeometrySet &geometry_set, if (normals) { bke::SpanAttributeWriter normals_writer = - attribute_accessor.lookup_or_add_for_write_span("N", ATTR_DOMAIN_POINT); + attribute_accessor.lookup_or_add_for_write_span("N", bke::AttrDomain::Point); MutableSpan point_normals = normals_writer.span; for (size_t i = 0; i < normals->size(); i++) { Imath::V3f nor_in = (*normals)[i]; diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc index e6c51b16c37..00c61e66cad 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.cc +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -164,8 +164,8 @@ void USDCurvesReader::read_curve_sample(Curves *cu, const double motionSampleTim if (usdWidths.size()) { bke::SpanAttributeWriter radii = - geometry.attributes_for_write().lookup_or_add_for_write_span("radius", - ATTR_DOMAIN_POINT); + geometry.attributes_for_write().lookup_or_add_for_write_span( + "radius", bke::AttrDomain::Point); for (const int i_point : geometry.points_range()) { radii.span[i_point] = usdWidths[i_point]; } -- 2.30.2 From cb9829f9bf671d890a9ba85918a5c10469e37485 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Wed, 10 Jan 2024 13:30:48 -0800 Subject: [PATCH 3/7] Fix USD Shape animations --- source/blender/io/usd/intern/usd_reader_shape.cc | 13 +++++++++++++ source/blender/io/usd/intern/usd_reader_shape.h | 8 ++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_shape.cc b/source/blender/io/usd/intern/usd_reader_shape.cc index 79bcc255271..98da19dc019 100644 --- a/source/blender/io/usd/intern/usd_reader_shape.cc +++ b/source/blender/io/usd/intern/usd_reader_shape.cc @@ -2,6 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_mesh.hh" #include "BKE_modifier.hh" @@ -161,6 +162,18 @@ Mesh *USDShapeReader::read_mesh(Mesh *existing_mesh, return active_mesh; } +void USDShapeReader::read_geometry(bke::GeometrySet &geometry_set, + USDMeshReadParams params, + const char **err_str) +{ + Mesh *existing_mesh = geometry_set.get_mesh_for_write(); + Mesh *new_mesh = read_mesh(existing_mesh, params, err_str); + + if (new_mesh != existing_mesh) { + geometry_set.replace_mesh(new_mesh); + } +} + Mesh *USDShapeReader::mesh_from_prim(Mesh *existing_mesh, double motionSampleTime, pxr::VtIntArray &face_indices, diff --git a/source/blender/io/usd/intern/usd_reader_shape.h b/source/blender/io/usd/intern/usd_reader_shape.h index 5aa69b7b423..3e499890c76 100644 --- a/source/blender/io/usd/intern/usd_reader_shape.h +++ b/source/blender/io/usd/intern/usd_reader_shape.h @@ -41,6 +41,8 @@ class USDShapeReader : public USDGeomReader { pxr::VtIntArray &face_indices, pxr::VtIntArray &face_counts) const; + Mesh *read_mesh(Mesh *existing_mesh, USDMeshReadParams params, const char ** /*err_str*/); + public: USDShapeReader(const pxr::UsdPrim &prim, const USDImportParams &import_params, @@ -50,10 +52,8 @@ class USDShapeReader : public USDGeomReader { void read_object_data(Main *bmain, double motionSampleTime) override; void read_geometry(bke::GeometrySet & /*geometry_set*/, USDMeshReadParams /*params*/, - const char ** /*err_str*/) override - { - } - Mesh *read_mesh(Mesh *existing_mesh, USDMeshReadParams params, const char ** /*err_str*/); + const char ** /*err_str*/) override; + bool is_time_varying(); virtual bool topology_changed(const Mesh * /*existing_mesh*/, -- 2.30.2 From 98134393c16164961a378db1d6bd0201ce761a07 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Wed, 7 Feb 2024 13:40:44 -0800 Subject: [PATCH 4/7] Fix previous merge issues --- source/blender/io/usd/usd.hh | 14 +++++++++----- .../modifiers/intern/MOD_meshsequencecache.cc | 6 ++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/source/blender/io/usd/usd.hh b/source/blender/io/usd/usd.hh index d82a8910b56..117a1f3bc27 100644 --- a/source/blender/io/usd/usd.hh +++ b/source/blender/io/usd/usd.hh @@ -17,6 +17,10 @@ struct Object; struct ReportList; struct wmJobWorkerStatus; +namespace blender::bke { +struct GeometrySet; +} + namespace blender::io::usd { /** @@ -170,11 +174,11 @@ void USD_free_handle(CacheArchiveHandle *handle); void USD_get_transform(CacheReader *reader, float r_mat[4][4], float time, float scale); /** Either modifies current_mesh in-place or constructs a new mesh. */ -Mesh *USD_read_mesh(CacheReader *reader, - Object *ob, - Mesh *existing_mesh, - USDMeshReadParams params, - const char **err_str); +void USD_read_geometry(CacheReader *reader, + Object *ob, + blender::bke::GeometrySet &geometry_set, + USDMeshReadParams params, + const char **err_str); bool USD_mesh_topology_changed(CacheReader *reader, const Object *ob, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 192d36611b6..82118b1baf5 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -234,8 +234,10 @@ static void modify_geometry_set(ModifierData *md, } case CACHEFILE_TYPE_USD: { # ifdef WITH_USD - const USDMeshReadParams params = create_mesh_read_params(time * FPS, mcmd->read_flag); - USD_read_geometry(mcmd->reader, ctx->object, *geometry_set, params, &err_str); + const blender::io::usd::USDMeshReadParams params = blender::io::usd::create_mesh_read_params( + time * FPS, mcmd->read_flag); + blender::io::usd::USD_read_geometry( + mcmd->reader, ctx->object, *geometry_set, params, &err_str); # endif break; } -- 2.30.2 From 531d178e80dedfb21f140efc415c8a73e104035d Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Tue, 20 Feb 2024 12:41:54 -0800 Subject: [PATCH 5/7] Fix Alembic crash when reading cyclic bezier curves --- .../io/alembic/intern/abc_reader_curves.cc | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index e64f8212924..ee0769696b8 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -8,23 +8,21 @@ #include "abc_reader_curves.h" #include "abc_axis_conversion.h" -#include "abc_reader_transform.h" #include "abc_util.h" #include -#include "MEM_guardedalloc.h" - #include "DNA_curves_types.h" #include "DNA_object_types.h" -#include "BLI_listbase.h" - +#include "BKE_attribute.hh" #include "BKE_curve.hh" #include "BKE_curves.hh" #include "BKE_geometry_set.hh" #include "BKE_object.hh" +#include "BLI_vector.hh" + #include "BLT_translation.hh" using Alembic::Abc::FloatArraySamplePtr; @@ -43,7 +41,6 @@ using Alembic::AbcGeom::ISampleSelector; using Alembic::AbcGeom::kWrapExisting; namespace blender::io::alembic { - static int16_t get_curve_resolution(const ICurvesSchema &schema, const Alembic::Abc::ISampleSelector &sample_sel) { @@ -99,7 +96,8 @@ static int get_curve_overlap(const Alembic::AbcGeom::CurvePeriodicity periodicit const int end = idx + num_verts; int overlap = 0; - for (int j = start, k = end - order; j < order; j++, k++) { + const int safe_order = order <= num_verts ? order : num_verts; + for (int j = start, k = end - safe_order; j < (start + safe_order); j++, k++) { const Imath::V3f &p1 = (*positions)[j]; const Imath::V3f &p2 = (*positions)[k]; @@ -168,7 +166,7 @@ struct PreprocessedSampleData { * actually starts, accounting for duplicate points indicating cyclicity. */ Vector offset_in_alembic; /* This holds one value for each spline to tell whether it is cyclic. */ - Vector curves_overlaps; + Vector curves_cyclic; /* This holds one value for each spline which define its order. */ Vector curves_orders; @@ -227,7 +225,7 @@ static std::optional preprocess_sample(StringRefNull iob /* Add 1 as these store offsets with the actual value being `offset[i + 1] - offset[i]`. */ data.offset_in_blender.resize(curve_count + 1); data.offset_in_alembic.resize(curve_count + 1); - data.curves_overlaps.resize(curve_count); + data.curves_cyclic.resize(curve_count); data.curve_type = get_curve_type(smp.getBasis()); if (data.curve_type == CURVE_TYPE_NURBS) { @@ -249,14 +247,14 @@ static std::optional preprocess_sample(StringRefNull iob data.offset_in_blender[i] = blender_offset; data.offset_in_alembic[i] = alembic_offset; - data.curves_overlaps[i] = overlap != 0; + data.curves_cyclic[i] = overlap != 0; if (data.curve_type == CURVE_TYPE_NURBS) { data.curves_orders[i] = curve_order; } - data.do_cyclic |= overlap != 0; - blender_offset += vertices_count - overlap; + data.do_cyclic |= data.curves_cyclic[i]; + blender_offset += (overlap >= vertices_count) ? vertices_count : (vertices_count - overlap); alembic_offset += vertices_count; } data.offset_in_blender[curve_count] = blender_offset; @@ -364,7 +362,9 @@ void AbcCurveReader::read_curves_sample(Curves *curves, } if (data.do_cyclic) { - geometry.cyclic_for_write().copy_from(data.curves_overlaps); + geometry.cyclic_for_write().copy_from(data.curves_cyclic); + geometry.handle_types_left_for_write().fill(BEZIER_HANDLE_AUTO); + geometry.handle_types_right_for_write().fill(BEZIER_HANDLE_AUTO); } if (data.radii) { -- 2.30.2 From 9e2eae5c2b140ca6901674ff3ecefa7e822645b5 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Tue, 20 Feb 2024 12:42:44 -0800 Subject: [PATCH 6/7] USD: read in all relevant bezier handle data properly --- .../blender/io/usd/intern/usd_reader_curve.cc | 208 ++++++++++++++---- 1 file changed, 162 insertions(+), 46 deletions(-) diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc index a2ca3fe16b3..dea1dd0084d 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.cc +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -1,4 +1,4 @@ -/* SPDX-FileCopyrightText: 2023 Blender Authors +/* SPDX-FileCopyrightText: 2024 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later * Adapted from the Blender Alembic importer implementation. Copyright 2016 Kévin Dietrich. @@ -6,51 +6,99 @@ #include "usd_reader_curve.hh" +#include "BKE_attribute.hh" #include "BKE_curves.hh" #include "BKE_geometry_set.hh" -#include "BKE_mesh.hh" #include "BKE_object.hh" -#include "BLI_listbase.h" +#include "BLI_index_range.hh" +#include "BLI_math_vector_types.hh" #include "DNA_curves_types.h" #include "DNA_object_types.h" -#include "MEM_guardedalloc.h" - #include #include -#include - #include #include namespace blender::io::usd { +static inline float3 to_float3(pxr::GfVec3f vec3f) +{ + return float3(vec3f.data()); +} + +static inline int bezier_point_count(int usd_count, bool is_cyclic) +{ + return is_cyclic ? (usd_count / 3) : ((usd_count / 3) + 1); +} + +static int point_count(int usdCount, CurveType curve_type, bool is_cyclic) +{ + if (curve_type == CURVE_TYPE_BEZIER) { + return bezier_point_count(usdCount, is_cyclic); + } + else { + return usdCount; + } +} /** Return the sum of the values of each element in `usdCounts`. This is used for precomputing the * total number of points for all curves in some curve primitive. */ -static int accumulate_point_count(const pxr::VtIntArray &usdCounts) +static int accumulate_point_count(const pxr::VtIntArray &usdCounts, + CurveType curve_type, + bool is_cyclic) { int result = 0; for (int v : usdCounts) { - result += v; + result += point_count(v, curve_type, is_cyclic); } return result; } +static void add_bezier_control_point(int cp, + int offset, + MutableSpan positions, + MutableSpan handles_left, + MutableSpan handles_right, + const Span &usdPoints) +{ + if (offset == 0) { + positions[cp] = to_float3(usdPoints[offset]); + handles_right[cp] = to_float3(usdPoints[offset + 1]); + handles_left[cp] = 2.0f * positions[cp] - handles_right[cp]; + } + else if (offset == usdPoints.size() - 1) { + positions[cp] = to_float3(usdPoints[offset]); + handles_left[cp] = to_float3(usdPoints[offset - 1]); + handles_right[cp] = 2.0f * positions[cp] - handles_left[cp]; + } + else { + positions[cp] = to_float3(usdPoints[offset]); + handles_left[cp] = to_float3(usdPoints[offset - 1]); + handles_right[cp] = to_float3(usdPoints[offset + 1]); + } +} + /** Returns true if the number of curves or the number of curve points in each curve differ. */ static bool curves_topology_changed(const CurvesGeometry &geometry, - const pxr::VtIntArray &usdCounts) + const pxr::VtIntArray &usdCounts, + CurveType curve_type, + int expected_total_point_num, + bool is_cyclic) { if (geometry.curve_num != usdCounts.size()) { return true; } + if (geometry.point_num != expected_total_point_num) { + return true; + } - for (int curve_idx = 0; curve_idx < geometry.curve_num; curve_idx++) { - const int num_in_usd = usdCounts[curve_idx]; - const int num_in_blender = geometry.curve_offsets[curve_idx]; + for (const int curve_idx : IndexRange(geometry.curve_num)) { + const int expected_curve_point_num = point_count(usdCounts[curve_idx], curve_type, is_cyclic); + const int current_curve_point_num = geometry.curve_offsets[curve_idx]; - if (num_in_usd != num_in_blender) { + if (current_curve_point_num != expected_curve_point_num) { return true; } } @@ -58,14 +106,20 @@ static bool curves_topology_changed(const CurvesGeometry &geometry, return false; } -static CurveType get_curve_type(pxr::TfToken basis) +static CurveType get_curve_type(pxr::TfToken type, pxr::TfToken basis) { - if (basis == pxr::UsdGeomTokens->bspline) { - return CURVE_TYPE_NURBS; - } - if (basis == pxr::UsdGeomTokens->bezier) { - return CURVE_TYPE_BEZIER; + if (type == pxr::UsdGeomTokens->cubic) { + if (basis == pxr::UsdGeomTokens->bezier) { + return CURVE_TYPE_BEZIER; + } + if (basis == pxr::UsdGeomTokens->bspline) { + return CURVE_TYPE_NURBS; + } + if (basis == pxr::UsdGeomTokens->catmullRom) { + return CURVE_TYPE_CATMULL_ROM; + } } + return CURVE_TYPE_POLY; } @@ -92,7 +146,6 @@ void USDCurvesReader::read_object_data(Main *bmain, double motionSampleTime) void USDCurvesReader::read_curve_sample(Curves *cu, const double motionSampleTime) { curve_prim_ = pxr::UsdGeomBasisCurves(prim_); - if (!curve_prim_) { return; } @@ -102,7 +155,6 @@ void USDCurvesReader::read_curve_sample(Curves *cu, const double motionSampleTim pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr(); pxr::VtIntArray usdCounts; - vertexAttr.Get(&usdCounts, motionSampleTime); pxr::VtVec3fArray usdPoints; @@ -123,21 +175,21 @@ void USDCurvesReader::read_curve_sample(Curves *cu, const double motionSampleTim pxr::TfToken wrap; wrapAttr.Get(&wrap, motionSampleTime); - bke::CurvesGeometry &geometry = cu->geometry.wrap(); + const CurveType curve_type = get_curve_type(type, basis); + const bool is_cyclic = wrap == pxr::UsdGeomTokens->periodic; const int num_subcurves = usdCounts.size(); - const int num_points = accumulate_point_count(usdCounts); + const int num_points = accumulate_point_count(usdCounts, curve_type, is_cyclic); + const int default_resolution = 6; - if (curves_topology_changed(geometry, usdCounts)) { + bke::CurvesGeometry &geometry = cu->geometry.wrap(); + if (curves_topology_changed(geometry, usdCounts, curve_type, num_points, is_cyclic)) { geometry.resize(num_points, num_subcurves); } - MutableSpan offsets = geometry.offsets_for_write(); - MutableSpan positions = geometry.positions_for_write(); - - const CurveType curve_type = get_curve_type(basis); geometry.fill_curve_types(curve_type); + geometry.resolution_for_write().fill(default_resolution); - if (wrap == pxr::UsdGeomTokens->periodic) { + if (is_cyclic) { geometry.cyclic_for_write().fill(true); } @@ -146,29 +198,93 @@ void USDCurvesReader::read_curve_sample(Curves *cu, const double motionSampleTim geometry.nurbs_orders_for_write().fill(curve_order); } - const int default_resolution = 2; - geometry.resolution_for_write().fill(default_resolution); + MutableSpan offsets = geometry.offsets_for_write(); + MutableSpan positions = geometry.positions_for_write(); - int offset = 0; - for (size_t i = 0; i < num_subcurves; i++) { - const int num_verts = usdCounts[i]; - offsets[i] = offset; - offset += num_verts; + /* Bezier curves require care in filing out their left/right handles. */ + if (type == pxr::UsdGeomTokens->cubic && basis == pxr::UsdGeomTokens->bezier) { + geometry.handle_types_left_for_write().fill(BEZIER_HANDLE_ALIGN); + geometry.handle_types_right_for_write().fill(BEZIER_HANDLE_ALIGN); + + MutableSpan handles_right = geometry.handle_positions_right_for_write(); + MutableSpan handles_left = geometry.handle_positions_left_for_write(); + Span points{usdPoints.data(), int64_t(usdPoints.size())}; + + int usd_point_offset = 0; + int point_offset = 0; + for (const int i : IndexRange(num_subcurves)) { + const int usd_point_count = usdCounts[i]; + const int point_count = bezier_point_count(usd_point_count, is_cyclic); + + offsets[i] = point_offset; + + int cp_offset = 0; + for (const int cp : IndexRange(point_count)) { + add_bezier_control_point(cp, + cp_offset, + positions.slice(point_offset, point_count), + handles_left.slice(point_offset, point_count), + handles_right.slice(point_offset, point_count), + points.slice(usd_point_offset, usd_point_count)); + cp_offset += 3; + } + + point_offset += point_count; + usd_point_offset += usd_point_count; + } + } + else { + int offset = 0; + for (const int i : IndexRange(num_subcurves)) { + const int num_verts = usdCounts[i]; + offsets[i] = offset; + offset += num_verts; + } + + for (const int i_point : geometry.points_range()) { + positions[i_point] = to_float3(usdPoints[i_point]); + } } - for (const int i_point : geometry.points_range()) { - positions[i_point][0] = float(usdPoints[i_point][0]); - positions[i_point][1] = float(usdPoints[i_point][1]); - positions[i_point][2] = float(usdPoints[i_point][2]); - } - - if (usdWidths.size()) { + if (!usdWidths.empty()) { bke::SpanAttributeWriter radii = geometry.attributes_for_write().lookup_or_add_for_write_span( "radius", bke::AttrDomain::Point); - for (const int i_point : geometry.points_range()) { - radii.span[i_point] = usdWidths[i_point]; + + pxr::TfToken widths_interp = curve_prim_.GetWidthsInterpolation(); + if (widths_interp == pxr::UsdGeomTokens->constant) { + radii.span.fill(usdWidths[0] / 2); } + else { + const bool is_bezier_vertex_interp = (type == pxr::UsdGeomTokens->cubic && + basis == pxr::UsdGeomTokens->bezier && + widths_interp == pxr::UsdGeomTokens->vertex); + if (is_bezier_vertex_interp) { + /* Blender does not support 'vertex-varying' interpolation. + * Assign the widths as-if it were 'varying' only. */ + int usd_point_offset = 0; + int point_offset = 0; + for (const int i : IndexRange(num_subcurves)) { + const int usd_point_count = usdCounts[i]; + const int point_count = bezier_point_count(usd_point_count, is_cyclic); + + int cp_offset = 0; + for (const int cp : IndexRange(point_count)) { + radii.span[point_offset + cp] = usdWidths[usd_point_offset + cp_offset] / 2; + cp_offset += 3; + } + + point_offset += point_count; + usd_point_offset += usd_point_count; + } + } + else { + for (const int i_point : geometry.points_range()) { + radii.span[i_point] = usdWidths[i_point] / 2; + } + } + } + radii.finish(); } } -- 2.30.2 From b9ec8722b40fa4e9ce4b6dac1b941c5f6b9d906a Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Tue, 27 Feb 2024 17:54:36 -0800 Subject: [PATCH 7/7] Formatting --- source/blender/io/alembic/intern/abc_reader_mesh.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index e891da8b659..b48d7965c78 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -28,8 +28,8 @@ #include "BLT_translation.hh" #include "BKE_attribute.hh" -#include "BKE_geometry_set.hh" #include "BKE_customdata.hh" +#include "BKE_geometry_set.hh" #include "BKE_lib_id.hh" #include "BKE_main.hh" #include "BKE_material.h" -- 2.30.2