This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/io/alembic/intern/alembic_capi.cc
Campbell Barton 525364be31 Cleanup: reduce indirect DNA header inclusion
Remove DNA headers, using forward declarations where possible.

Also removed duplicate header, header including it's self
and unnecessary inclusion of libc system headers from BKE header.
2020-12-15 12:34:14 +11:00

997 lines
29 KiB
C++

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup balembic
*/
#include "../ABC_alembic.h"
#include <Alembic/AbcMaterial/IMaterial.h>
#include "abc_axis_conversion.h"
#include "abc_reader_archive.h"
#include "abc_reader_camera.h"
#include "abc_reader_curves.h"
#include "abc_reader_mesh.h"
#include "abc_reader_nurbs.h"
#include "abc_reader_points.h"
#include "abc_reader_transform.h"
#include "abc_util.h"
#include "MEM_guardedalloc.h"
#include "DNA_cachefile_types.h"
#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_cachefile.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_global.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "ED_undo.h"
#include "BLI_compiler_compat.h"
#include "BLI_fileops.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "WM_api.h"
#include "WM_types.h"
using Alembic::Abc::IV3fArrayProperty;
using Alembic::Abc::ObjectHeader;
using Alembic::Abc::PropertyHeader;
using Alembic::Abc::V3fArraySamplePtr;
using Alembic::AbcGeom::ICamera;
using Alembic::AbcGeom::ICurves;
using Alembic::AbcGeom::IFaceSet;
using Alembic::AbcGeom::ILight;
using Alembic::AbcGeom::INuPatch;
using Alembic::AbcGeom::IObject;
using Alembic::AbcGeom::IPoints;
using Alembic::AbcGeom::IPolyMesh;
using Alembic::AbcGeom::IPolyMeshSchema;
using Alembic::AbcGeom::ISampleSelector;
using Alembic::AbcGeom::ISubD;
using Alembic::AbcGeom::IXform;
using Alembic::AbcGeom::kWrapExisting;
using Alembic::AbcGeom::MetaData;
using Alembic::AbcMaterial::IMaterial;
using namespace blender::io::alembic;
struct AbcArchiveHandle {
int unused;
};
BLI_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle)
{
return reinterpret_cast<ArchiveReader *>(handle);
}
BLI_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive)
{
return reinterpret_cast<AbcArchiveHandle *>(archive);
}
//#define USE_NURBS
/* NOTE: this function is similar to visit_objects below, need to keep them in
* sync. */
static bool gather_objects_paths(const IObject &object, ListBase *object_paths)
{
if (!object.valid()) {
return false;
}
size_t children_claiming_this_object = 0;
size_t num_children = object.getNumChildren();
for (size_t i = 0; i < num_children; i++) {
bool child_claims_this_object = gather_objects_paths(object.getChild(i), object_paths);
children_claiming_this_object += child_claims_this_object ? 1 : 0;
}
const MetaData &md = object.getMetaData();
bool get_path = false;
bool parent_is_part_of_this_object = false;
if (!object.getParent()) {
/* The root itself is not an object we should import. */
}
else if (IXform::matches(md)) {
if (has_property(object.getProperties(), "locator")) {
get_path = true;
}
else {
get_path = children_claiming_this_object == 0;
}
/* Transforms are never "data" for their parent. */
parent_is_part_of_this_object = false;
}
else {
/* These types are "data" for their parent. */
get_path = IPolyMesh::matches(md) || ISubD::matches(md) ||
#ifdef USE_NURBS
INuPatch::matches(md) ||
#endif
ICamera::matches(md) || IPoints::matches(md) || ICurves::matches(md);
parent_is_part_of_this_object = get_path;
}
if (get_path) {
void *abc_path_void = MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath");
AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(abc_path_void);
BLI_strncpy(abc_path->path, object.getFullName().c_str(), sizeof(abc_path->path));
BLI_addtail(object_paths, abc_path);
}
return parent_is_part_of_this_object;
}
AbcArchiveHandle *ABC_create_handle(struct Main *bmain,
const char *filename,
ListBase *object_paths)
{
ArchiveReader *archive = new ArchiveReader(bmain, filename);
if (!archive->valid()) {
delete archive;
return nullptr;
}
if (object_paths) {
gather_objects_paths(archive->getTop(), object_paths);
}
return handle_from_archive(archive);
}
void ABC_free_handle(AbcArchiveHandle *handle)
{
delete archive_from_handle(handle);
}
int ABC_get_version()
{
return ALEMBIC_LIBRARY_VERSION;
}
static void find_iobject(const IObject &object, IObject &ret, const std::string &path)
{
if (!object.valid()) {
return;
}
std::vector<std::string> tokens;
split(path, '/', tokens);
IObject tmp = object;
std::vector<std::string>::iterator iter;
for (iter = tokens.begin(); iter != tokens.end(); ++iter) {
IObject child = tmp.getChild(*iter);
tmp = child;
}
ret = tmp;
}
/* ********************** Import file ********************** */
/**
* Generates an AbcObjectReader for this Alembic object and its children.
*
* \param object: The Alembic IObject to visit.
* \param readers: The created AbcObjectReader * will be appended to this vector.
* \param settings: Import settings, not used directly but passed to the
* AbcObjectReader subclass constructors.
* \param r_assign_as_parent: Return parameter, contains a list of reader
* pointers, whose parent pointer should still be set.
* This is filled when this call to visit_object() didn't create
* a reader that should be the parent.
* \return A pair of boolean and reader pointer. The boolean indicates whether
* this IObject claims its parent as part of the same object
* (for example an IPolyMesh object would claim its parent, as the mesh
* is interpreted as the object's data, and the parent IXform as its
* Blender object). The pointer is the AbcObjectReader that represents
* the IObject parameter.
*
* NOTE: this function is similar to gather_object_paths above, need to keep
* them in sync. */
static std::pair<bool, AbcObjectReader *> visit_object(
const IObject &object,
AbcObjectReader::ptr_vector &readers,
ImportSettings &settings,
AbcObjectReader::ptr_vector &r_assign_as_parent)
{
const std::string &full_name = object.getFullName();
if (!object.valid()) {
std::cerr << " - " << full_name << ": object is invalid, skipping it and all its children.\n";
return std::make_pair(false, static_cast<AbcObjectReader *>(nullptr));
}
/* The interpretation of data by the children determine the role of this
* object. This is especially important for Xform objects, as they can be
* either part of a Blender object or a Blender object (Empty) themselves.
*/
size_t children_claiming_this_object = 0;
size_t num_children = object.getNumChildren();
AbcObjectReader::ptr_vector claiming_child_readers;
AbcObjectReader::ptr_vector nonclaiming_child_readers;
AbcObjectReader::ptr_vector assign_as_parent;
for (size_t i = 0; i < num_children; i++) {
const IObject ichild = object.getChild(i);
/* TODO: When we only support C++11, use std::tie() instead. */
std::pair<bool, AbcObjectReader *> child_result;
child_result = visit_object(ichild, readers, settings, assign_as_parent);
bool child_claims_this_object = child_result.first;
AbcObjectReader *child_reader = child_result.second;
if (child_reader == nullptr) {
BLI_assert(!child_claims_this_object);
}
else {
if (child_claims_this_object) {
claiming_child_readers.push_back(child_reader);
}
else {
nonclaiming_child_readers.push_back(child_reader);
}
}
children_claiming_this_object += child_claims_this_object ? 1 : 0;
}
BLI_assert(children_claiming_this_object == claiming_child_readers.size());
AbcObjectReader *reader = nullptr;
const MetaData &md = object.getMetaData();
bool parent_is_part_of_this_object = false;
if (!object.getParent()) {
/* The root itself is not an object we should import. */
}
else if (IXform::matches(md)) {
bool create_empty;
/* An xform can either be a Blender Object (if it contains a mesh, for
* example), but it can also be an Empty. Its correct translation to
* Blender's data model depends on its children. */
/* Check whether or not this object is a Maya locator, which is
* similar to empties used as parent object in Blender. */
if (has_property(object.getProperties(), "locator")) {
create_empty = true;
}
else {
create_empty = claiming_child_readers.empty();
}
if (create_empty) {
reader = new AbcEmptyReader(object, settings);
}
}
else if (IPolyMesh::matches(md)) {
reader = new AbcMeshReader(object, settings);
parent_is_part_of_this_object = true;
}
else if (ISubD::matches(md)) {
reader = new AbcSubDReader(object, settings);
parent_is_part_of_this_object = true;
}
else if (INuPatch::matches(md)) {
#ifdef USE_NURBS
/* TODO(kevin): importing cyclic NURBS from other software crashes
* at the moment. This is due to the fact that NURBS in other
* software have duplicated points which causes buffer overflows in
* Blender. Need to figure out exactly how these points are
* duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
* Until this is fixed, disabling NURBS reading. */
reader = new AbcNurbsReader(object, settings);
parent_is_part_of_this_object = true;
#endif
}
else if (ICamera::matches(md)) {
reader = new AbcCameraReader(object, settings);
parent_is_part_of_this_object = true;
}
else if (IPoints::matches(md)) {
reader = new AbcPointsReader(object, settings);
parent_is_part_of_this_object = true;
}
else if (IMaterial::matches(md)) {
/* Pass for now. */
}
else if (ILight::matches(md)) {
/* Pass for now. */
}
else if (IFaceSet::matches(md)) {
/* Pass, those are handled in the mesh reader. */
}
else if (ICurves::matches(md)) {
reader = new AbcCurveReader(object, settings);
parent_is_part_of_this_object = true;
}
else {
std::cerr << "Alembic object " << full_name << " is of unsupported schema type '"
<< object.getMetaData().get("schemaObjTitle") << "'" << std::endl;
}
if (reader) {
/* We have created a reader, which should imply that this object is
* not claimed as part of any child Alembic object. */
BLI_assert(claiming_child_readers.empty());
readers.push_back(reader);
reader->incref();
AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
BLI_strncpy(abc_path->path, full_name.c_str(), sizeof(abc_path->path));
BLI_addtail(&settings.cache_file->object_paths, abc_path);
/* We can now assign this reader as parent for our children. */
if (nonclaiming_child_readers.size() + assign_as_parent.size() > 0) {
for (AbcObjectReader *child_reader : nonclaiming_child_readers) {
child_reader->parent_reader = reader;
}
for (AbcObjectReader *child_reader : assign_as_parent) {
child_reader->parent_reader = reader;
}
}
}
else if (object.getParent()) {
if (!claiming_child_readers.empty()) {
/* The first claiming child will serve just fine as parent to
* our non-claiming children. Since all claiming children share
* the same XForm, it doesn't really matter which one we pick. */
AbcObjectReader *claiming_child = claiming_child_readers[0];
for (AbcObjectReader *child_reader : nonclaiming_child_readers) {
child_reader->parent_reader = claiming_child;
}
for (AbcObjectReader *child_reader : assign_as_parent) {
child_reader->parent_reader = claiming_child;
}
/* Claiming children should have our parent set as their parent. */
for (AbcObjectReader *child_reader : claiming_child_readers) {
r_assign_as_parent.push_back(child_reader);
}
}
else {
/* This object isn't claimed by any child, and didn't produce
* a reader. Odd situation, could be the top Alembic object, or
* an unsupported Alembic schema. Delegate to our parent. */
for (AbcObjectReader *child_reader : claiming_child_readers) {
r_assign_as_parent.push_back(child_reader);
}
for (AbcObjectReader *child_reader : nonclaiming_child_readers) {
r_assign_as_parent.push_back(child_reader);
}
for (AbcObjectReader *child_reader : assign_as_parent) {
r_assign_as_parent.push_back(child_reader);
}
}
}
return std::make_pair(parent_is_part_of_this_object, reader);
}
enum {
ABC_NO_ERROR = 0,
ABC_ARCHIVE_FAIL,
};
struct ImportJobData {
bContext *C;
Main *bmain;
Scene *scene;
ViewLayer *view_layer;
wmWindowManager *wm;
char filename[1024];
ImportSettings settings;
ArchiveReader *archive;
std::vector<AbcObjectReader *> readers;
short *stop;
short *do_update;
float *progress;
char error_code;
bool was_cancelled;
bool import_ok;
bool is_background_job;
};
static void import_startjob(void *user_data, short *stop, short *do_update, float *progress)
{
SCOPE_TIMER("Alembic import, objects reading and creation");
ImportJobData *data = static_cast<ImportJobData *>(user_data);
data->stop = stop;
data->do_update = do_update;
data->progress = progress;
WM_set_locked_interface(data->wm, true);
ArchiveReader *archive = new ArchiveReader(data->bmain, data->filename);
if (!archive->valid()) {
data->error_code = ABC_ARCHIVE_FAIL;
delete archive;
return;
}
CacheFile *cache_file = static_cast<CacheFile *>(
BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename)));
/* Decrement the ID ref-count because it is going to be incremented for each
* modifier and constraint that it will be attached to, so since currently
* it is not used by anyone, its use count will off by one. */
id_us_min(&cache_file->id);
cache_file->is_sequence = data->settings.is_sequence;
cache_file->scale = data->settings.scale;
STRNCPY(cache_file->filepath, data->filename);
data->archive = archive;
data->settings.cache_file = cache_file;
*data->do_update = true;
*data->progress = 0.05f;
/* Parse Alembic Archive. */
AbcObjectReader::ptr_vector assign_as_parent;
visit_object(archive->getTop(), data->readers, data->settings, assign_as_parent);
/* There shouldn't be any orphans. */
BLI_assert(assign_as_parent.empty());
if (G.is_break) {
data->was_cancelled = true;
return;
}
*data->do_update = true;
*data->progress = 0.1f;
/* Create objects and set scene frame range. */
const float size = static_cast<float>(data->readers.size());
size_t i = 0;
chrono_t min_time = std::numeric_limits<chrono_t>::max();
chrono_t max_time = std::numeric_limits<chrono_t>::min();
ISampleSelector sample_sel(0.0f);
std::vector<AbcObjectReader *>::iterator iter;
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
AbcObjectReader *reader = *iter;
if (reader->valid()) {
reader->readObjectData(data->bmain, sample_sel);
min_time = std::min(min_time, reader->minTime());
max_time = std::max(max_time, reader->maxTime());
}
else {
std::cerr << "Object " << reader->name() << " in Alembic file " << data->filename
<< " is invalid.\n";
}
*data->progress = 0.1f + 0.3f * (++i / size);
*data->do_update = true;
if (G.is_break) {
data->was_cancelled = true;
return;
}
}
if (data->settings.set_frame_range) {
Scene *scene = data->scene;
if (data->settings.is_sequence) {
SFRA = data->settings.sequence_offset;
EFRA = SFRA + (data->settings.sequence_len - 1);
CFRA = SFRA;
}
else if (min_time < max_time) {
SFRA = static_cast<int>(round(min_time * FPS));
EFRA = static_cast<int>(round(max_time * FPS));
CFRA = SFRA;
}
}
/* Setup parenthood. */
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
const AbcObjectReader *reader = *iter;
const AbcObjectReader *parent_reader = reader->parent_reader;
Object *ob = reader->object();
if (parent_reader == nullptr || !reader->inherits_xform()) {
ob->parent = nullptr;
}
else {
ob->parent = parent_reader->object();
}
}
/* Setup transformations and constraints. */
i = 0;
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
AbcObjectReader *reader = *iter;
reader->setupObjectTransform(0.0f);
*data->progress = 0.7f + 0.3f * (++i / size);
*data->do_update = true;
if (G.is_break) {
data->was_cancelled = true;
return;
}
}
}
static void import_endjob(void *user_data)
{
SCOPE_TIMER("Alembic import, cleanup");
ImportJobData *data = static_cast<ImportJobData *>(user_data);
std::vector<AbcObjectReader *>::iterator iter;
/* Delete objects on cancellation. */
if (data->was_cancelled) {
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
Object *ob = (*iter)->object();
/* It's possible that cancellation occurred between the creation of
* the reader and the creation of the Blender object. */
if (ob == nullptr) {
continue;
}
BKE_id_free_us(data->bmain, ob);
}
}
else {
/* Add object to scene. */
Base *base;
LayerCollection *lc;
ViewLayer *view_layer = data->view_layer;
BKE_view_layer_base_deselect_all(view_layer);
lc = BKE_layer_collection_get_active(view_layer);
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
Object *ob = (*iter)->object();
BKE_collection_object_add(data->bmain, lc->collection, ob);
base = BKE_view_layer_base_find(view_layer, ob);
/* TODO: is setting active needed? */
BKE_view_layer_base_select_and_set_active(view_layer, base);
DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE);
DEG_id_tag_update_ex(data->bmain,
&ob->id,
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION |
ID_RECALC_BASE_FLAGS);
}
DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(data->bmain);
if (data->is_background_job) {
/* Blender already returned from the import operator, so we need to store our own extra undo
* step. */
ED_undo_push(data->C, "Alembic Import Finished");
}
}
for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
AbcObjectReader *reader = *iter;
reader->decref();
if (reader->refcount() == 0) {
delete reader;
}
}
WM_set_locked_interface(data->wm, false);
switch (data->error_code) {
default:
case ABC_NO_ERROR:
data->import_ok = !data->was_cancelled;
break;
case ABC_ARCHIVE_FAIL:
WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail.");
break;
}
WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene);
}
static void import_freejob(void *user_data)
{
ImportJobData *data = static_cast<ImportJobData *>(user_data);
delete data->archive;
delete data;
}
bool ABC_import(bContext *C,
const char *filepath,
float scale,
bool is_sequence,
bool set_frame_range,
int sequence_len,
int offset,
bool validate_meshes,
bool as_background_job)
{
/* Using new here since MEM_* functions do not call constructor to properly initialize data. */
ImportJobData *job = new ImportJobData();
job->C = C;
job->bmain = CTX_data_main(C);
job->scene = CTX_data_scene(C);
job->view_layer = CTX_data_view_layer(C);
job->wm = CTX_wm_manager(C);
job->import_ok = false;
BLI_strncpy(job->filename, filepath, 1024);
job->settings.scale = scale;
job->settings.is_sequence = is_sequence;
job->settings.set_frame_range = set_frame_range;
job->settings.sequence_len = sequence_len;
job->settings.sequence_offset = offset;
job->settings.validate_meshes = validate_meshes;
job->error_code = ABC_NO_ERROR;
job->was_cancelled = false;
job->archive = nullptr;
job->is_background_job = as_background_job;
G.is_break = false;
bool import_ok = false;
if (as_background_job) {
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
job->scene,
"Alembic Import",
WM_JOB_PROGRESS,
WM_JOB_TYPE_ALEMBIC);
/* setup job */
WM_jobs_customdata_set(wm_job, job, import_freejob);
WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
WM_jobs_callbacks(wm_job, import_startjob, nullptr, nullptr, import_endjob);
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
else {
/* Fake a job context, so that we don't need NULL pointer checks while importing. */
short stop = 0, do_update = 0;
float progress = 0.0f;
import_startjob(job, &stop, &do_update, &progress);
import_endjob(job);
import_ok = job->import_ok;
import_freejob(job);
}
return import_ok;
}
/* ************************************************************************** */
void ABC_get_transform(CacheReader *reader, float r_mat_world[4][4], float time, float scale)
{
if (!reader) {
return;
}
AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
bool is_constant = false;
/* Convert from the local matrix we obtain from Alembic to world coordinates
* for Blender. This conversion is done here rather than by Blender due to
* work around the non-standard interpretation of CONSTRAINT_SPACE_LOCAL in
* BKE_constraint_mat_convertspace(). */
Object *object = abc_reader->object();
if (object->parent == nullptr) {
/* No parent, so local space is the same as world space. */
abc_reader->read_matrix(r_mat_world, time, scale, is_constant);
return;
}
float mat_parent[4][4];
BKE_object_get_parent_matrix(object, object->parent, mat_parent);
float mat_local[4][4];
abc_reader->read_matrix(mat_local, time, scale, is_constant);
mul_m4_m4m4(r_mat_world, mat_parent, object->parentinv);
mul_m4_m4m4(r_mat_world, r_mat_world, mat_local);
}
/* ************************************************************************** */
static AbcObjectReader *get_abc_reader(CacheReader *reader, Object *ob, const char **err_str)
{
AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
IObject iobject = abc_reader->iobject();
if (!iobject.valid()) {
*err_str = "Invalid object: verify object path";
return nullptr;
}
const ObjectHeader &header = iobject.getHeader();
if (!abc_reader->accepts_object_type(header, ob, err_str)) {
/* err_str is set by acceptsObjectType() */
return nullptr;
}
return abc_reader;
}
static ISampleSelector sample_selector_for_time(float time)
{
/* kFloorIndex is used to be compatible with non-interpolating
* properties; they use the floor. */
return ISampleSelector(time, ISampleSelector::kFloorIndex);
}
Mesh *ABC_read_mesh(CacheReader *reader,
Object *ob,
Mesh *existing_mesh,
const float time,
const char **err_str,
int read_flag)
{
AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str);
if (abc_reader == nullptr) {
return nullptr;
}
ISampleSelector sample_sel = sample_selector_for_time(time);
return abc_reader->read_mesh(existing_mesh, sample_sel, read_flag, err_str);
}
bool ABC_mesh_topology_changed(
CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str)
{
AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str);
if (abc_reader == nullptr) {
return false;
}
ISampleSelector sample_sel = sample_selector_for_time(time);
return abc_reader->topology_changed(existing_mesh, sample_sel);
}
/* ************************************************************************** */
void CacheReader_free(CacheReader *reader)
{
AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
abc_reader->decref();
if (abc_reader->refcount() == 0) {
delete abc_reader;
}
}
void CacheReader_incref(CacheReader *reader)
{
AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
abc_reader->incref();
}
CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle,
CacheReader *reader,
Object *object,
const char *object_path)
{
if (object_path[0] == '\0') {
return reader;
}
ArchiveReader *archive = archive_from_handle(handle);
if (!archive || !archive->valid()) {
return reader;
}
IObject iobject;
find_iobject(archive->getTop(), iobject, object_path);
if (reader) {
CacheReader_free(reader);
}
ImportSettings settings;
AbcObjectReader *abc_reader = create_reader(iobject, settings);
if (abc_reader == nullptr) {
/* This object is not supported */
return nullptr;
}
abc_reader->object(object);
abc_reader->incref();
return reinterpret_cast<CacheReader *>(abc_reader);
}
/* ************************************************************************** */
static const PropertyHeader *get_property_header(const IPolyMeshSchema &schema, const char *name)
{
const PropertyHeader *prop_header = schema.getPropertyHeader(name);
if (prop_header) {
return prop_header;
}
ICompoundProperty prop = schema.getArbGeomParams();
if (!has_property(prop, name)) {
return nullptr;
}
return prop.getPropertyHeader(name);
}
bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name)
{
AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
if (!abc_reader) {
return false;
}
IObject iobject = abc_reader->iobject();
if (!iobject.valid()) {
return false;
}
const ObjectHeader &header = iobject.getHeader();
if (!IPolyMesh::matches(header)) {
return false;
}
IPolyMesh mesh(iobject, kWrapExisting);
IPolyMeshSchema schema = mesh.getSchema();
const PropertyHeader *prop_header = get_property_header(schema, name);
if (!prop_header) {
return false;
}
return IV3fArrayProperty::matches(prop_header->getMetaData());
}
static V3fArraySamplePtr get_velocity_prop(const IPolyMeshSchema &schema,
const ISampleSelector &iss,
const std::string &name)
{
const PropertyHeader *prop_header = schema.getPropertyHeader(name);
if (prop_header) {
const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0);
return velocity_prop.getValue(iss);
}
ICompoundProperty prop = schema.getArbGeomParams();
if (!has_property(prop, name)) {
return V3fArraySamplePtr();
}
const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0);
if (velocity_prop) {
return velocity_prop.getValue(iss);
}
return V3fArraySamplePtr();
}
int ABC_read_velocity_cache(CacheReader *reader,
const char *velocity_name,
const float time,
float velocity_scale,
int num_vertices,
float *r_vertex_velocities)
{
AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
if (!abc_reader) {
return -1;
}
IObject iobject = abc_reader->iobject();
if (!iobject.valid()) {
return -1;
}
const ObjectHeader &header = iobject.getHeader();
if (!IPolyMesh::matches(header)) {
return -1;
}
IPolyMesh mesh(iobject, kWrapExisting);
IPolyMeshSchema schema = mesh.getSchema();
ISampleSelector sample_sel(time);
const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel);
V3fArraySamplePtr velocities = get_velocity_prop(schema, sample_sel, velocity_name);
if (!velocities) {
return -1;
}
float vel[3];
int num_velocity_vectors = static_cast<int>(velocities->size());
if (num_velocity_vectors != num_vertices) {
return -1;
}
for (size_t i = 0; i < velocities->size(); ++i) {
const Imath::V3f &vel_in = (*velocities)[i];
copy_zup_from_yup(vel, vel_in.getValue());
mul_v3_fl(vel, velocity_scale);
copy_v3_v3(r_vertex_velocities + i * 3, vel);
}
return num_vertices;
}