Alembic/USD: use geometry sets to import data #115623
|
@ -1276,7 +1276,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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -8,24 +8,23 @@
|
|||
|
||||
#include "abc_reader_curves.h"
|
||||
#include "abc_axis_conversion.h"
|
||||
#include "abc_reader_transform.h"
|
||||
#include "abc_util.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#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_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"
|
||||
|
||||
#include "BKE_curve.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
using Alembic::Abc::FloatArraySamplePtr;
|
||||
using Alembic::Abc::Int32ArraySamplePtr;
|
||||
using Alembic::Abc::P3fArraySamplePtr;
|
||||
|
@ -42,6 +41,239 @@ 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)
|
||||
{
|
||||
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;
|
||||
|
||||
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];
|
||||
|
||||
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<int> preprocessed_offsets)
|
||||
{
|
||||
/* Offsets have an extra element. */
|
||||
if (geometry.curve_num != preprocessed_offsets.size() - 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Span<int> 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<int> 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<int> offset_in_alembic;
|
||||
/* This holds one value for each spline to tell whether it is cyclic. */
|
||||
Vector<bool> curves_cyclic;
|
||||
/* This holds one value for each spline which define its order. */
|
||||
Vector<int8_t> 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<PreprocessedSampleData> 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_cyclic.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_cyclic[i] = overlap != 0;
|
||||
|
||||
if (data.curve_type == CURVE_TYPE_NURBS) {
|
||||
data.curves_orders[i] = curve_order;
|
||||
}
|
||||
|
||||
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;
|
||||
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 +296,13 @@ bool AbcCurveReader::accepts_object_type(
|
|||
{
|
||||
if (!Alembic::AbcGeom::ICurves::matches(alembic_header)) {
|
||||
*err_str = RPT_(
|
||||
"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 = RPT_("Object type mismatch, Alembic object path points to Curves");
|
||||
if (ob->type != OB_CURVES) {
|
||||
*err_str = RPT_("Object type mismatch, Alembic object path points to Curves.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -79,262 +311,103 @@ 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<Curves *>(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<PreprocessedSampleData> 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<Nurb *>(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<float3> 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_cyclic);
|
||||
geometry.handle_types_left_for_write().fill(BEZIER_HANDLE_AUTO);
|
||||
geometry.handle_types_right_for_write().fill(BEZIER_HANDLE_AUTO);
|
||||
}
|
||||
|
||||
if (data.radii) {
|
||||
bke::SpanAttributeWriter<float> radii =
|
||||
geometry.attributes_for_write().lookup_or_add_for_write_span<float>(
|
||||
"radius", bke::AttrDomain::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<float> curves_weights = geometry.nurbs_weights_for_write();
|
||||
Span<float> 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<BPoint *>(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<float *>(
|
||||
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 = RPT_("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<Curve *>(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<Nurb *>(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<Nurb *>(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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_material.h"
|
||||
|
@ -687,6 +688,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,
|
||||
|
@ -1099,4 +1118,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -142,14 +142,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*/,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,19 @@
|
|||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_pointcloud.hh"
|
||||
|
||||
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 +56,8 @@ bool AbcPointsReader::accepts_object_type(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (ob->type != OB_MESH) {
|
||||
*err_str = RPT_("Object type mismatch, Alembic object path points to Points");
|
||||
if (ob->type != OB_POINTCLOUD) {
|
||||
*err_str = RPT_("Object type mismatch, Alembic object path points to Points.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -68,29 +66,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<PointCloud *>(
|
||||
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<bke::PointCloudComponent>().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,
|
||||
ImportSettings *settings)
|
||||
static void read_points(const P3fArraySamplePtr positions, MutableSpan<float3> 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<float3> r_points)
|
||||
{
|
||||
Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector);
|
||||
|
||||
|
@ -109,23 +116,19 @@ void read_points_sample(const IPointsSchema &schema,
|
|||
}
|
||||
}
|
||||
|
||||
read_mverts(*config.mesh, positions, vnormals);
|
||||
|
||||
if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) {
|
||||
V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name);
|
||||
if (velocities) {
|
||||
read_velocity(velocities, config, settings->velocity_scale);
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
@ -137,26 +140,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->verts_num != 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();
|
||||
}
|
||||
|
||||
ImportSettings settings;
|
||||
settings.velocity_name = velocity_name;
|
||||
settings.velocity_scale = velocity_scale;
|
||||
if (point_cloud->totpoint != positions->size()) {
|
||||
point_cloud = BKE_pointcloud_new_nomain(positions->size());
|
||||
}
|
||||
|
||||
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, &settings);
|
||||
bke::MutableAttributeAccessor attribute_accessor = point_cloud->attributes_for_write();
|
||||
|
||||
return mesh_to_export;
|
||||
bke::SpanAttributeWriter<float3> positions_writer =
|
||||
attribute_accessor.lookup_or_add_for_write_span<float3>("position", bke::AttrDomain::Point);
|
||||
MutableSpan<float3> point_positions = positions_writer.span;
|
||||
N3fArraySamplePtr normals = read_points_sample(m_schema, sample_sel, point_positions);
|
||||
positions_writer.finish();
|
||||
|
||||
bke::SpanAttributeWriter<float> point_radii_writer =
|
||||
attribute_accessor.lookup_or_add_for_write_span<float>("radius", bke::AttrDomain::Point);
|
||||
MutableSpan<float> 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<float3> normals_writer =
|
||||
attribute_accessor.lookup_or_add_for_write_span<float3>("N", bke::AttrDomain::Point);
|
||||
MutableSpan<float3> 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
|
||||
|
|
|
@ -26,17 +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,
|
||||
ImportSettings *settings);
|
||||
|
||||
} // namespace blender::io::alembic
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -574,19 +574,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<USDGeomReader *>(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,
|
||||
|
|
|
@ -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,41 +6,134 @@
|
|||
|
||||
#include "usd_reader_curve.hh"
|
||||
|
||||
#include "BKE_curve.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_curves_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include <pxr/base/vt/array.h>
|
||||
#include <pxr/base/vt/types.h>
|
||||
#include <pxr/base/vt/value.h>
|
||||
|
||||
#include <pxr/usd/usdGeom/basisCurves.h>
|
||||
#include <pxr/usd/usdGeom/curves.h>
|
||||
|
||||
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,
|
||||
CurveType curve_type,
|
||||
bool is_cyclic)
|
||||
{
|
||||
int result = 0;
|
||||
for (int v : usdCounts) {
|
||||
result += point_count(v, curve_type, is_cyclic);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void add_bezier_control_point(int cp,
|
||||
int offset,
|
||||
MutableSpan<float3> positions,
|
||||
MutableSpan<float3> handles_left,
|
||||
MutableSpan<float3> handles_right,
|
||||
const Span<pxr::GfVec3f> &usdPoints)
|
||||
{
|
||||
if (offset == 0) {
|
||||
deadpin marked this conversation as resolved
Outdated
|
||||
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,
|
||||
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 (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 (current_curve_point_num != expected_curve_point_num) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static CurveType get_curve_type(pxr::TfToken type, pxr::TfToken basis)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void USDCurvesReader::create_object(Main *bmain, const double /*motionSampleTime*/)
|
||||
{
|
||||
curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVES_LEGACY);
|
||||
curve_ = static_cast<Curves *>(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,10 +143,9 @@ 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_);
|
||||
|
||||
if (!curve_prim_) {
|
||||
return;
|
||||
}
|
||||
|
@ -63,9 +155,7 @@ void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime
|
|||
pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr();
|
||||
|
||||
pxr::VtIntArray usdCounts;
|
||||
|
||||
vertexAttr.Get(&usdCounts, motionSampleTime);
|
||||
int num_subcurves = usdCounts.size();
|
||||
|
||||
pxr::VtVec3fArray usdPoints;
|
||||
pointsAttr.Get(&usdPoints, motionSampleTime);
|
||||
|
@ -85,155 +175,134 @@ 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);
|
||||
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, curve_type, is_cyclic);
|
||||
const int default_resolution = 6;
|
||||
|
||||
/* 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;
|
||||
bke::CurvesGeometry &geometry = cu->geometry.wrap();
|
||||
if (curves_topology_changed(geometry, usdCounts, curve_type, num_points, is_cyclic)) {
|
||||
geometry.resize(num_points, num_subcurves);
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i < num_subcurves; i++) {
|
||||
const int num_verts = usdCounts[i];
|
||||
Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), __func__));
|
||||
geometry.fill_curve_types(curve_type);
|
||||
geometry.resolution_for_write().fill(default_resolution);
|
||||
|
||||
if (basis == pxr::UsdGeomTokens->bspline) {
|
||||
nu->flag = CU_SMOOTH;
|
||||
nu->type = CU_NURBS;
|
||||
}
|
||||
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;
|
||||
if (is_cyclic) {
|
||||
geometry.cyclic_for_write().fill(true);
|
||||
}
|
||||
|
||||
nu->pntsu = num_verts;
|
||||
nu->pntsv = 1;
|
||||
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);
|
||||
}
|
||||
|
||||
if (type == pxr::UsdGeomTokens->cubic) {
|
||||
nu->orderu = 4;
|
||||
}
|
||||
else if (type == pxr::UsdGeomTokens->linear) {
|
||||
nu->orderu = 2;
|
||||
}
|
||||
MutableSpan<int> offsets = geometry.offsets_for_write();
|
||||
MutableSpan<float3> positions = geometry.positions_for_write();
|
||||
|
||||
if (wrap == pxr::UsdGeomTokens->periodic) {
|
||||
nu->flagu |= CU_NURB_CYCLIC;
|
||||
}
|
||||
else if (wrap == pxr::UsdGeomTokens->pinned) {
|
||||
nu->flagu |= CU_NURB_ENDPOINT;
|
||||
}
|
||||
/* 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);
|
||||
|
||||
float weight = 1.0f;
|
||||
MutableSpan<float3> handles_right = geometry.handle_positions_right_for_write();
|
||||
MutableSpan<float3> handles_left = geometry.handle_positions_left_for_write();
|
||||
Span<pxr::GfVec3f> points{usdPoints.data(), int64_t(usdPoints.size())};
|
||||
|
||||
nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, __func__));
|
||||
BPoint *bp = nu->bp;
|
||||
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);
|
||||
|
||||
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;
|
||||
offsets[i] = point_offset;
|
||||
|
||||
float radius = curve_->offset;
|
||||
if (idx < usdWidths.size()) {
|
||||
radius = usdWidths[idx];
|
||||
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;
|
||||
}
|
||||
|
||||
bp->radius = radius;
|
||||
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;
|
||||
}
|
||||
|
||||
BKE_nurb_knot_calc_u(nu);
|
||||
BKE_nurb_knot_calc_v(nu);
|
||||
for (const int i_point : geometry.points_range()) {
|
||||
positions[i_point] = to_float3(usdPoints[i_point]);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_addtail(BKE_curve_nurbs_get(cu), nu);
|
||||
if (!usdWidths.empty()) {
|
||||
bke::SpanAttributeWriter<float> radii =
|
||||
geometry.attributes_for_write().lookup_or_add_for_write_span<float>(
|
||||
"radius", bke::AttrDomain::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();
|
||||
}
|
||||
}
|
||||
|
||||
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<Curve *>(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<Nurb *>(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<Nurb *>(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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*/)
|
||||
{
|
||||
|
|
|
@ -12,6 +12,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"
|
||||
|
@ -1122,6 +1123,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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "usd_reader_nurbs.hh"
|
||||
|
||||
#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*/)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#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,
|
||||
|
|
|
@ -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,
|
||||
|
@ -48,9 +50,10 @@ 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;
|
||||
|
||||
bool is_time_varying();
|
||||
|
||||
virtual bool topology_changed(const Mesh * /*existing_mesh*/,
|
||||
|
|
|
@ -19,6 +19,10 @@ struct Object;
|
|||
struct ReportList;
|
||||
struct wmJobWorkerStatus;
|
||||
|
||||
namespace blender::bke {
|
||||
struct GeometrySet;
|
||||
}
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
/**
|
||||
|
@ -172,11 +176,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,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BKE_cachefile.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_lib_query.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
|
@ -55,8 +56,7 @@
|
|||
# include "usd.hh"
|
||||
#endif
|
||||
|
||||
using blender::float3;
|
||||
using blender::Span;
|
||||
using namespace blender;
|
||||
|
||||
static void init_data(ModifierData *md)
|
||||
{
|
||||
|
@ -163,6 +163,91 @@ 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<MeshSeqCacheModifierData *>(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()) {
|
||||
deadpin marked this conversation as resolved
Outdated
Jesse Yurkovich
commented
Keep this message generic since USD is here now. Keep this message generic since USD is here now.
|
||||
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 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;
|
||||
}
|
||||
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)
|
||||
|
@ -225,43 +310,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 blender::io::usd::USDMeshReadParams params = blender::io::usd::create_mesh_read_params(
|
||||
time * FPS, mcmd->read_flag);
|
||||
result = blender::io::usd::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<bke::MeshComponent>().release();
|
||||
|
||||
if (!ELEM(result, nullptr, mesh) && (mesh != org_mesh)) {
|
||||
BKE_id_free(nullptr, mesh);
|
||||
|
@ -443,7 +495,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,
|
||||
|
|
Loading…
Reference in New Issue
There's a comment in
main
about/* TODO(makowalski): Beziers are not properly imported as beziers. */
-- Is that still applicable or is that actually ok now? Guessing we need a sample file to check?I don't know what problem this is referring to, so test files would be appreciated.