Changeset View
Standalone View
source/blender/alembic/intern/abc_object.cc
| Show First 20 Lines • Show All 208 Lines • ▼ Show 20 Lines | Imath::M44d get_matrix(const IXformSchema &schema, const float time) | ||||
| if (i0 != i1) { | if (i0 != i1) { | ||||
| schema.get(s1, Alembic::AbcGeom::ISampleSelector(i1)); | schema.get(s1, Alembic::AbcGeom::ISampleSelector(i1)); | ||||
| return blend_matrices(s0.getMatrix(), s1.getMatrix(), weight); | return blend_matrices(s0.getMatrix(), s1.getMatrix(), weight); | ||||
| } | } | ||||
| return s0.getMatrix(); | return s0.getMatrix(); | ||||
| } | } | ||||
| void AbcObjectReader::readObjectMatrix(const float time) | void AbcObjectReader::setupObjectTransform(const float time) | ||||
| { | { | ||||
| bool is_constant = false; | bool is_constant = false; | ||||
| this->read_matrix(m_object->obmat, time, m_settings->scale, is_constant); | this->read_matrix(m_object->obmat, time, m_settings->scale, is_constant); | ||||
| invert_m4_m4(m_object->imat, m_object->obmat); | invert_m4_m4(m_object->imat, m_object->obmat); | ||||
| BKE_object_apply_mat4(m_object, m_object->obmat, false, false); | BKE_object_apply_mat4(m_object, m_object->obmat, false, false); | ||||
| if (!is_constant) { | if (!is_constant) { | ||||
| bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE); | bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE); | ||||
| bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data); | bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data); | ||||
| BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX); | BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX); | ||||
| data->cache_file = m_settings->cache_file; | data->cache_file = m_settings->cache_file; | ||||
| id_us_plus(&data->cache_file->id); | id_us_plus(&data->cache_file->id); | ||||
| data->reader = reinterpret_cast<CacheReader *>(this); | data->reader = reinterpret_cast<CacheReader *>(this); | ||||
| this->incref(); | this->incref(); | ||||
| } | } | ||||
| } | } | ||||
| void AbcObjectReader::read_matrix(float mat[4][4], const float time, const float scale, bool &is_constant) | Alembic::AbcGeom::IXform AbcObjectReader::xform() | ||||
| { | { | ||||
| IXform ixform; | |||||
| bool has_alembic_parent = false; | |||||
| /* Check that we have an empty object (locator, bone head/tail...). */ | /* Check that we have an empty object (locator, bone head/tail...). */ | ||||
| if (IXform::matches(m_iobject.getMetaData())) { | if (IXform::matches(m_iobject.getMetaData())) { | ||||
| ixform = IXform(m_iobject, Alembic::AbcGeom::kWrapExisting); | return IXform(m_iobject, Alembic::AbcGeom::kWrapExisting); | ||||
| /* See comment below. */ | |||||
| has_alembic_parent = m_iobject.getParent().getParent().valid(); | |||||
| } | } | ||||
| /* Check that we have an object with actual data. */ | |||||
| else if (IXform::matches(m_iobject.getParent().getMetaData())) { | /* Check that we have an object with actual data, in which case the | ||||
| ixform = IXform(m_iobject.getParent(), Alembic::AbcGeom::kWrapExisting); | * parent Alembic object should contain the transform. */ | ||||
| IObject abc_parent = m_iobject.getParent(); | |||||
| /* This is a bit hackish, but we need to make sure that extra | |||||
| * transformations added to the matrix (rotation/scale) are only applied | /* The archive's top object can be recognised by not having a parent. */ | ||||
| * to root objects. The way objects and their hierarchy are created will | if (abc_parent.getParent() | ||||
| * need to be revisited at some point but for now this seems to do the | && IXform::matches(abc_parent.getMetaData())) { | ||||
| * trick. | return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting); | ||||
| * | |||||
| * Explanation of the trick: | |||||
| * The first getParent() will return this object's transformation matrix. | |||||
| * The second getParent() will get the parent of the transform, but this | |||||
| * might be the archive root ('/') which is valid, so we go passed it to | |||||
| * make sure that there is no parent. | |||||
| */ | |||||
| has_alembic_parent = m_iobject.getParent().getParent().getParent().valid(); | |||||
| } | } | ||||
| /* Should not happen. */ | /* Should not happen. */ | ||||
| else { | std::cerr << "AbcObjectReader::xform(): " | ||||
| << "unable to find IXform for Alembic object '" | |||||
| << m_iobject.getFullName() << "'\n"; | |||||
| BLI_assert(false); | |||||
| return IXform(); | |||||
| } | |||||
| void AbcObjectReader::read_matrix(float r_mat[4][4], const float time, | |||||
dfelinto: nitpicking, single line between functions | |||||
| const float scale, bool &is_constant) | |||||
Done Inline Actionsreturn parameters can/should have a r_ prefix dfelinto: return parameters can/should have a r_ prefix | |||||
| { | |||||
| IXform ixform = xform(); | |||||
| if (!ixform) { | |||||
| return; | return; | ||||
| } | } | ||||
| const IXformSchema &schema(ixform.getSchema()); | const IXformSchema & schema(ixform.getSchema()); | ||||
| if (!schema.valid()) { | if (!schema.valid()) { | ||||
| std::cerr << "Alembic object " << ixform.getFullName() | |||||
Not Done Inline ActionsIf the user should now that, shouldn't we have a way to tell her that there is a message in console? std:cerr is fine and all, but the average user will never even be aware of what happened dfelinto: If the user should now that, shouldn't we have a way to tell her that there is a message in… | |||||
Not Done Inline ActionsI've been thinking of some proper error reporting. In these cases, since the export will pretty much always be part of a job (except when launched through Python), I think we could set some boolean value and in export_endjob(...) in alembic_capi.cc use WM_report(...) to tell the user to check the console or so. Something similar is done for the importer already. Also, we should avoid using std::cerr (or std::cout), especially in a separate thread, since it is a global variable; better accumulate all the errors into a single std::ostream and pass everything in one go to std::cerr at the end. I can look into it if you guys want. kevindietrich: I've been thinking of some proper error reporting. In these cases, since the export will pretty… | |||||
Not Done Inline ActionsI think doing error reporting in a better way is a good idea. However, I think it's also out of scope of these changes, which are intended to fix bugs in Alembic (not necessarily improve usability yet). @Kévin Dietrich (kevindietrich) if you can look into this, great. sybren: I think doing error reporting in a better way is a good idea. However, I think it's also out of… | |||||
| << " has an invalid schema." << std::endl; | |||||
| return; | return; | ||||
| } | } | ||||
| bool has_alembic_parent; | |||||
| IObject ixform_parent = ixform.getParent(); | |||||
| if (!ixform_parent.getParent()) { | |||||
| /* The archive top object certainly is not a transform itself, so handle | |||||
| * it as "no parent". */ | |||||
| has_alembic_parent = false; | |||||
| } | |||||
| else { | |||||
| /* Sybren: getInhertisXforms() should be a const function, see Alembic | |||||
| * pull request at https://github.com/alembic/alembic/pull/111 and | |||||
| * bug report at https://github.com/alembic/alembic/issues/110 */ | |||||
| IXformSchema & schema_mutable = const_cast<IXformSchema &>(schema); | |||||
| has_alembic_parent = ixform_parent && schema_mutable.getInheritsXforms(); | |||||
| } | |||||
| const Imath::M44d matrix = get_matrix(schema, time); | const Imath::M44d matrix = get_matrix(schema, time); | ||||
| convert_matrix(matrix, m_object, mat, scale, has_alembic_parent); | convert_matrix(matrix, m_object, r_mat); | ||||
| if (has_alembic_parent) { | |||||
| /* In this case, the matrix in Alembic is in local coordinates, so | |||||
| * convert to world matrix. To prevent us from reading and accumulating | |||||
| * all parent matrices in the Alembic file, we assume that the Blender | |||||
| * parent object is already updated for the current timekey, and use its | |||||
| * world matrix. */ | |||||
| BLI_assert(m_object->parent); | |||||
| mul_m4_m4m4(r_mat, m_object->parent->obmat, r_mat); | |||||
| } | |||||
| else { | |||||
| /* Only apply scaling to root objects, parenting will propagate it. */ | |||||
| float scale_mat[4][4]; | |||||
| scale_m4_fl(scale_mat, scale); | |||||
| scale_mat[3][3] = scale; /* scale translations too */ | |||||
| mul_m4_m4m4(r_mat, r_mat, scale_mat); | |||||
| } | |||||
| is_constant = schema.isConstant(); | is_constant = schema.isConstant(); | ||||
| } | } | ||||
| void AbcObjectReader::addCacheModifier() | void AbcObjectReader::addCacheModifier() | ||||
| { | { | ||||
| ModifierData *md = modifier_new(eModifierType_MeshSequenceCache); | ModifierData *md = modifier_new(eModifierType_MeshSequenceCache); | ||||
| BLI_addtail(&m_object->modifiers, md); | BLI_addtail(&m_object->modifiers, md); | ||||
| Show All 36 Lines | |||||
nitpicking, single line between functions