Changeset View
Changeset View
Standalone View
Standalone View
source/blender/alembic/intern/alembic_capi.cc
| Show First 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | if (IXform::matches(md)) { | ||||
| /* Avoid creating an empty object if the child of this transform | /* Avoid creating an empty object if the child of this transform | ||||
| * is not a transform (that is an empty). */ | * is not a transform (that is an empty). */ | ||||
| if (child.getNumChildren() == 1) { | if (child.getNumChildren() == 1) { | ||||
| if (IXform::matches(child.getChild(0).getMetaData())) { | if (IXform::matches(child.getChild(0).getMetaData())) { | ||||
| get_path = true; | get_path = true; | ||||
| } | } | ||||
| #if 0 | #if 0 | ||||
| else { | else { | ||||
| std::cerr << "Skipping " << child.getFullName() << '\n'; | std::cerr << "gather_objects_paths(" << object.getFullName() << "): Skipping " << child.getFullName() << '\n'; | ||||
| } | } | ||||
| #endif | #endif | ||||
| } | } | ||||
| else { | else { | ||||
| get_path = true; | get_path = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 207 Lines • ▼ Show 20 Lines | void ABC_export( | ||||
| WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); | WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); | ||||
| WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); | WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); | ||||
| WM_jobs_start(CTX_wm_manager(C), wm_job); | WM_jobs_start(CTX_wm_manager(C), wm_job); | ||||
| } | } | ||||
| /* ********************** Import file ********************** */ | /* ********************** Import file ********************** */ | ||||
| static void visit_object(const IObject &object, | /** | ||||
| * Generates an AbcObjectReader for this Alembic object and its children. | |||||
dfelinto: make it bool! | |||||
| * | |||||
| * \param object The Alembic IObject to visit. | |||||
| * \param readers The created AbcObjectReader * will be appended to this vector. | |||||
| * \param readers_map The created AbcObjectReader * will be appended to this | |||||
| * map, keyed by its full name in Alembic. | |||||
| * \param settings Import settings, not used directly but passed to the | |||||
| * AbcObjectReader subclass constructors. | |||||
| * \return 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). | |||||
| */ | |||||
| static bool visit_object(const IObject &object, | |||||
| std::vector<AbcObjectReader *> &readers, | std::vector<AbcObjectReader *> &readers, | ||||
| GHash *parent_map, | GHash * readers_map, | ||||
| ImportSettings &settings) | ImportSettings &settings) | ||||
| { | { | ||||
| const std::string & full_name = object.getFullName(); | |||||
| if (!object.valid()) { | if (!object.valid()) { | ||||
| return; | std::cerr << " - " << full_name << ": object is invalid, skipping it and all its children.\n"; | ||||
| return false; | |||||
| } | } | ||||
| for (int i = 0; i < object.getNumChildren(); ++i) { | // The interpretation of data by the children determine the role of this object. | ||||
| IObject child = object.getChild(i); | // This is especially important for Xform objects, as they can be either part of a Blender object | ||||
| // or a Blender object (empty) themselves. | |||||
| if (!child.valid()) { | size_t children_claiming_this_object = 0; | ||||
| continue; | size_t num_children = object.getNumChildren(); | ||||
| for (size_t i = 0; i < num_children; ++i) { | |||||
| bool child_claims_this_object = visit_object(object.getChild(i), readers, readers_map, settings); | |||||
| children_claiming_this_object += child_claims_this_object ? 1 : 0; | |||||
| } | } | ||||
| AbcObjectReader *reader = NULL; | AbcObjectReader *reader = NULL; | ||||
| const MetaData &md = child.getMetaData(); | const MetaData &md = object.getMetaData(); | ||||
| // const std::string schema_obj_title = md.get("schemaObjTitle"); | |||||
| if (IXform::matches(md)) { | // std::cerr << " - " << object.getFullName() << "(" << schema_obj_title << "): "; | ||||
| bool create_xform = false; | |||||
| bool parent_is_part_of_this_object = false; | |||||
| if (!object.getParent()) { | |||||
| // The root itself is not an object we should import. | |||||
Done Inline Actions- children_claiming_this_object += child_claims_this_object; + children_claiming_this_object += child_claims_this_object ? 1 : 0; dfelinto: ```
- children_claiming_this_object += child_claims_this_object;
+… | |||||
| } | |||||
| else if (IXform::matches(md)) { | |||||
| bool create_empty; | |||||
| /* An xform can either be a Blender Object (if it contains a mesh, for exapmle), | |||||
| * 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 | /* Check whether or not this object is a Maya locator, which is | ||||
| * similar to empties used as parent object in Blender. */ | * similar to empties used as parent object in Blender. */ | ||||
| if (has_property(child.getProperties(), "locator")) { | if (has_property(object.getProperties(), "locator")) { | ||||
| create_xform = true; | // std::cerr << "Maya locator; "; | ||||
| create_empty = true; | |||||
| } | } | ||||
| else { | else { | ||||
| /* Avoid creating an empty object if the child of this transform | // if (children_claiming_this_object > 0) { | ||||
| * is not a transform (that is an empty). */ | // std::cerr << children_claiming_this_object << " of its " << num_children | ||||
Done Inline Actionsthere are cases when this may happen without being an error. e.g.,: Rinky with hair and a child object. dfelinto: there are cases when this may happen without being an error. e.g.,: Rinky with hair and a child… | |||||
| if (child.getNumChildren() == 1) { | // << " children used this Xform for themselves, so not creating empty\n"; | ||||
| if (IXform::matches(child.getChild(0).getMetaData())) { | // } | ||||
| create_xform = true; | // else { | ||||
| } | // std::cerr << "None of its " << num_children | ||||
| #if 0 | // << " children used this Xform for themselves, creating empty\n"; | ||||
| else { | // } | ||||
| std::cerr << "Skipping " << child.getFullName() << '\n'; | create_empty = children_claiming_this_object == 0; | ||||
| } | } | ||||
| #endif | |||||
| } | if (create_empty) { | ||||
| else { | reader = new AbcEmptyReader(object, settings); | ||||
| create_xform = true; | // std::cerr << "created an AbcEmptyReader " | ||||
| } | // << reader->object_name() | ||||
Done Inline ActionsThis overwrites the create_empty condition set on line 431 LazyDodo: This overwrites the create_empty condition set on line 431 | |||||
| } | // << " with data " << reader->data_name(); | ||||
| if (create_xform) { | |||||
| reader = new AbcEmptyReader(child, settings); | |||||
| } | } | ||||
| } | } | ||||
| else if (IPolyMesh::matches(md)) { | else if (IPolyMesh::matches(md)) { | ||||
| reader = new AbcMeshReader(child, settings); | reader = new AbcMeshReader(object, settings); | ||||
| parent_is_part_of_this_object = true; | |||||
| // std::cerr << "created an AbcMeshReader " | |||||
| // << reader->object_name() | |||||
| // << " with data " << reader->data_name(); | |||||
| } | } | ||||
| else if (ISubD::matches(md)) { | else if (ISubD::matches(md)) { | ||||
| reader = new AbcSubDReader(child, settings); | reader = new AbcSubDReader(object, settings); | ||||
| parent_is_part_of_this_object = true; | |||||
| // std::cerr << "created an AbcSubDReader " | |||||
| // << reader->object_name() | |||||
| // << " with data " << reader->data_name(); | |||||
| } | } | ||||
| else if (INuPatch::matches(md)) { | else if (INuPatch::matches(md)) { | ||||
| #ifdef USE_NURBS | #ifdef USE_NURBS | ||||
| /* TODO(kevin): importing cyclic NURBS from other software crashes | /* TODO(kevin): importing cyclic NURBS from other software crashes | ||||
| * at the moment. This is due to the fact that NURBS in other | * at the moment. This is due to the fact that NURBS in other | ||||
| * software have duplicated points which causes buffer overflows in | * software have duplicated points which causes buffer overflows in | ||||
| * Blender. Need to figure out exactly how these points are | * Blender. Need to figure out exactly how these points are | ||||
| * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). | * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). | ||||
| * Until this is fixed, disabling NURBS reading. */ | * Until this is fixed, disabling NURBS reading. */ | ||||
| reader = new AbcNurbsReader(child, settings); | reader = new AbcNurbsReader(object, settings); | ||||
| parent_is_part_of_this_object = true; | |||||
| #endif | #endif | ||||
| } | } | ||||
| else if (ICamera::matches(md)) { | else if (ICamera::matches(md)) { | ||||
| reader = new AbcCameraReader(child, settings); | reader = new AbcCameraReader(object, settings); | ||||
| parent_is_part_of_this_object = true; | |||||
| // std::cerr << "created an AbcCameraReader " | |||||
| // << reader->object_name() | |||||
| // << " with data " << reader->data_name(); | |||||
| } | } | ||||
| else if (IPoints::matches(md)) { | else if (IPoints::matches(md)) { | ||||
| reader = new AbcPointsReader(child, settings); | reader = new AbcPointsReader(object, settings); | ||||
| parent_is_part_of_this_object = true; | |||||
| // std::cerr << "created an AbcPointsReader " | |||||
| // << reader->object_name() | |||||
| // << " with data " << reader->data_name(); | |||||
| } | } | ||||
| else if (IMaterial::matches(md)) { | else if (IMaterial::matches(md)) { | ||||
| /* Pass for now. */ | /* Pass for now. */ | ||||
| // std::cerr << "skipping IMaterial objects"; | |||||
| } | } | ||||
| else if (ILight::matches(md)) { | else if (ILight::matches(md)) { | ||||
| /* Pass for now. */ | /* Pass for now. */ | ||||
| // std::cerr << "skipping ILight objects"; | |||||
| } | } | ||||
| else if (IFaceSet::matches(md)) { | else if (IFaceSet::matches(md)) { | ||||
| /* Pass, those are handled in the mesh reader. */ | /* Pass, those are handled in the mesh reader. */ | ||||
| // std::cerr << "skipping IFaceSet objects"; | |||||
| } | } | ||||
| else if (ICurves::matches(md)) { | else if (ICurves::matches(md)) { | ||||
| reader = new AbcCurveReader(child, settings); | reader = new AbcCurveReader(object, settings); | ||||
| parent_is_part_of_this_object = true; | |||||
| // std::cerr << "created an AbcCurveReader " | |||||
| // << reader->object_name() | |||||
| // << " with data " << reader->data_name(); | |||||
| } | } | ||||
| else { | else { | ||||
| assert(false); | std::cerr << "Alembic object " << full_name | ||||
| << " is of unsupported schema type '" | |||||
| << object.getMetaData().get("schemaObjTitle") << "'" | |||||
| << std::endl; | |||||
| } | } | ||||
| // std::cerr << std::endl; | |||||
| if (reader) { | if (reader) { | ||||
| readers.push_back(reader); | readers.push_back(reader); | ||||
| reader->incref(); | reader->incref(); | ||||
| AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( | AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( | ||||
| MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); | MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); | ||||
| BLI_strncpy(abc_path->path, full_name.c_str(), PATH_MAX); | |||||
| BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX); | |||||
| BLI_addtail(&settings.cache_file->object_paths, abc_path); | BLI_addtail(&settings.cache_file->object_paths, abc_path); | ||||
| /* Cast to `void *` explicitly to avoid compiler errors because it | /* We have to take a copy of the name, because Alembic can reuse | ||||
| * is a `const char *` which the compiler cast to `const void *` | * memory, for example when dealing with instances. */ | ||||
| * instead of the expected `void *`. */ | char * name_copy = static_cast<char *>(MEM_mallocN( | ||||
| BLI_ghash_insert(parent_map, (void *)child.getFullName().c_str(), reader); | full_name.length() + 1, | ||||
| "Alembic readers_map key")); | |||||
| BLI_strncpy(name_copy, full_name.c_str(), full_name.length() + 1); | |||||
| BLI_ghash_insert(readers_map, name_copy, reader); | |||||
Done Inline Actionsadd a comment please, and re-consider ghash, we use it everywhere in Blender dfelinto: add a comment please, and re-consider ghash, we use it everywhere in Blender | |||||
| } | } | ||||
| visit_object(child, readers, parent_map, settings); | return parent_is_part_of_this_object; | ||||
| } | |||||
| } | } | ||||
| enum { | enum { | ||||
| ABC_NO_ERROR = 0, | ABC_NO_ERROR = 0, | ||||
| ABC_ARCHIVE_FAIL, | ABC_ARCHIVE_FAIL, | ||||
| }; | }; | ||||
| struct ImportJobData { | struct ImportJobData { | ||||
| Main *bmain; | Main *bmain; | ||||
| Scene *scene; | Scene *scene; | ||||
| char filename[1024]; | char filename[1024]; | ||||
| ImportSettings settings; | ImportSettings settings; | ||||
| GHash *parent_map; | GHash * readers_map; | ||||
| std::vector<AbcObjectReader *> readers; | std::vector<AbcObjectReader *> readers; | ||||
| short *stop; | short *stop; | ||||
| short *do_update; | short *do_update; | ||||
| float *progress; | float *progress; | ||||
| char error_code; | char error_code; | ||||
| bool was_cancelled; | bool was_cancelled; | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | static void import_startjob(void *user_data, short *stop, short *do_update, float *progress) | ||||
| cache_file->handle = handle_from_archive(archive); | cache_file->handle = handle_from_archive(archive); | ||||
| BLI_strncpy(cache_file->filepath, data->filename, 1024); | BLI_strncpy(cache_file->filepath, data->filename, 1024); | ||||
| data->settings.cache_file = cache_file; | data->settings.cache_file = cache_file; | ||||
| *data->do_update = true; | *data->do_update = true; | ||||
| *data->progress = 0.05f; | *data->progress = 0.05f; | ||||
| data->parent_map = BLI_ghash_str_new("alembic parent ghash"); | data->readers_map = BLI_ghash_str_new("Alembic readers_map ghash"); | ||||
| /* Parse Alembic Archive. */ | /* Parse Alembic Archive. */ | ||||
| visit_object(archive->getTop(), data->readers, data->readers_map, data->settings); | |||||
| visit_object(archive->getTop(), data->readers, data->parent_map, data->settings); | |||||
| if (G.is_break) { | if (G.is_break) { | ||||
| data->was_cancelled = true; | data->was_cancelled = true; | ||||
| return; | return; | ||||
| } | } | ||||
| *data->do_update = true; | *data->do_update = true; | ||||
| *data->progress = 0.1f; | *data->progress = 0.1f; | ||||
| /* Create objects and set scene frame range. */ | /* Create objects and set scene frame range. */ | ||||
| const float size = static_cast<float>(data->readers.size()); | const float size = static_cast<float>(data->readers.size()); | ||||
| size_t i = 0; | size_t i = 0; | ||||
| chrono_t min_time = std::numeric_limits<chrono_t>::max(); | chrono_t min_time = std::numeric_limits<chrono_t>::max(); | ||||
| chrono_t max_time = std::numeric_limits<chrono_t>::min(); | chrono_t max_time = std::numeric_limits<chrono_t>::min(); | ||||
| std::vector<AbcObjectReader *>::iterator iter; | std::vector<AbcObjectReader *>::iterator iter; | ||||
| for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { | for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { | ||||
| AbcObjectReader *reader = *iter; | AbcObjectReader *reader = *iter; | ||||
| if (reader->valid()) { | if (reader->valid()) { | ||||
| reader->readObjectData(data->bmain, 0.0f); | reader->readObjectData(data->bmain, 0.0f); | ||||
| reader->readObjectMatrix(0.0f); | |||||
| min_time = std::min(min_time, reader->minTime()); | min_time = std::min(min_time, reader->minTime()); | ||||
| max_time = std::max(max_time, reader->maxTime()); | 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.6f * (++i / size); | *data->progress = 0.1f + 0.3f * (++i / size); | ||||
| *data->do_update = true; | *data->do_update = true; | ||||
| if (G.is_break) { | if (G.is_break) { | ||||
| data->was_cancelled = true; | data->was_cancelled = true; | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| Show All 15 Lines | static void import_startjob(void *user_data, short *stop, short *do_update, float *progress) | ||||
| /* Setup parentship. */ | /* Setup parentship. */ | ||||
| i = 0; | i = 0; | ||||
| for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { | for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { | ||||
| const AbcObjectReader *reader = *iter; | const AbcObjectReader *reader = *iter; | ||||
| const AbcObjectReader *parent_reader = NULL; | const AbcObjectReader *parent_reader = NULL; | ||||
| const IObject &iobject = reader->iobject(); | const IObject &iobject = reader->iobject(); | ||||
| IObject parent = iobject.getParent(); | /* Find the parent reader by going up in the Alembic hierarchy until we find it. | ||||
| * Some Xform Alembic objects do not produce an AbcEmptyReader, since they | |||||
| if (!IXform::matches(iobject.getHeader())) { | * translate to a Blender object with a reader attached to the Xform's child. */ | ||||
| /* In the case of an non XForm node, the parent is the transform | IObject alembic_parent = iobject.getParent(); | ||||
| * matrix of the data itself, so we get the its grand parent. | |||||
| */ | |||||
| /* Special case with object only containing a mesh and some strands, | while (alembic_parent) { | ||||
| * we want both objects to be parented to the same object. */ | const char * parent_name = alembic_parent.getFullName().c_str(); | ||||
| if (!is_mesh_and_strands(parent)) { | parent_reader = reinterpret_cast<AbcObjectReader *>( | ||||
| parent = parent.getParent(); | BLI_ghash_lookup(data->readers_map, | ||||
| } | parent_name)); | ||||
| if (parent_reader != NULL) { | |||||
| break; // found the parent reader. | |||||
| } | } | ||||
| parent_reader = reinterpret_cast<AbcObjectReader *>( | alembic_parent = alembic_parent.getParent(); | ||||
| BLI_ghash_lookup(data->parent_map, parent.getFullName().c_str())); | } | ||||
| if (parent_reader) { | if (parent_reader) { | ||||
| Object *parent = parent_reader->object(); | Object *blender_parent = parent_reader->object(); | ||||
| if (parent != NULL && reader->object() != parent) { | if (blender_parent != NULL && reader->object() != blender_parent) { | ||||
| Object *ob = reader->object(); | Object *ob = reader->object(); | ||||
| ob->parent = parent; | ob->parent = blender_parent; | ||||
| } | } | ||||
| } | } | ||||
| *data->progress = 0.4f + 0.3f * (++i / size); | |||||
| *data->do_update = true; | |||||
| if (G.is_break) { | |||||
| data->was_cancelled = true; | |||||
| return; | |||||
| } | |||||
| } | |||||
| /* 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->progress = 0.7f + 0.3f * (++i / size); | ||||
| *data->do_update = true; | *data->do_update = true; | ||||
| if (G.is_break) { | if (G.is_break) { | ||||
| data->was_cancelled = true; | data->was_cancelled = true; | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| Show All 38 Lines | for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { | ||||
| AbcObjectReader *reader = *iter; | AbcObjectReader *reader = *iter; | ||||
| reader->decref(); | reader->decref(); | ||||
| if (reader->refcount() == 0) { | if (reader->refcount() == 0) { | ||||
| delete reader; | delete reader; | ||||
| } | } | ||||
| } | } | ||||
| if (data->parent_map) { | if (data->readers_map) { | ||||
| BLI_ghash_free(data->parent_map, NULL, NULL); | BLI_ghash_free(data->readers_map, MEM_freeN, NULL); | ||||
| } | } | ||||
| switch (data->error_code) { | switch (data->error_code) { | ||||
| default: | default: | ||||
| case ABC_NO_ERROR: | case ABC_NO_ERROR: | ||||
| break; | break; | ||||
| case ABC_ARCHIVE_FAIL: | case ABC_ARCHIVE_FAIL: | ||||
| WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail."); | WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail."); | ||||
| Show All 19 Lines | void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes) | ||||
| BLI_strncpy(job->filename, filepath, 1024); | BLI_strncpy(job->filename, filepath, 1024); | ||||
| job->settings.scale = scale; | job->settings.scale = scale; | ||||
| job->settings.is_sequence = is_sequence; | job->settings.is_sequence = is_sequence; | ||||
| job->settings.set_frame_range = set_frame_range; | job->settings.set_frame_range = set_frame_range; | ||||
| job->settings.sequence_len = sequence_len; | job->settings.sequence_len = sequence_len; | ||||
| job->settings.offset = offset; | job->settings.offset = offset; | ||||
| job->settings.validate_meshes = validate_meshes; | job->settings.validate_meshes = validate_meshes; | ||||
| job->parent_map = NULL; | job->readers_map = NULL; | ||||
| job->error_code = ABC_NO_ERROR; | job->error_code = ABC_NO_ERROR; | ||||
| job->was_cancelled = false; | job->was_cancelled = false; | ||||
| G.is_break = false; | G.is_break = false; | ||||
| wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), | wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), | ||||
| CTX_wm_window(C), | CTX_wm_window(C), | ||||
| job->scene, | job->scene, | ||||
| ▲ Show 20 Lines • Show All 120 Lines • Show Last 20 Lines | |||||
make it bool!