Merge branch 'master' into blender2.8
# Conflicts: # source/blender/alembic/intern/abc_exporter.cc # source/blender/alembic/intern/abc_exporter.h
This commit is contained in:
commit
781108cdc6
|
@ -176,11 +176,10 @@ def draw(layout, context, context_member, property_type, use_edit=True):
|
|||
if not is_rna:
|
||||
props = row.operator("wm.properties_edit", text="Edit")
|
||||
assign_props(props, val_draw, key)
|
||||
else:
|
||||
row.label(text="API Defined")
|
||||
|
||||
props = row.operator("wm.properties_remove", text="", icon='ZOOMOUT')
|
||||
assign_props(props, val_draw, key)
|
||||
else:
|
||||
row.label(text="API Defined")
|
||||
|
||||
|
||||
class PropertyPanel:
|
||||
|
|
|
@ -53,41 +53,57 @@ struct AlembicExportParams {
|
|||
double shutter_open;
|
||||
double shutter_close;
|
||||
|
||||
/* bools */
|
||||
unsigned int selected_only : 1;
|
||||
unsigned int uvs : 1;
|
||||
unsigned int normals : 1;
|
||||
unsigned int vcolors : 1;
|
||||
unsigned int apply_subdiv : 1;
|
||||
unsigned int flatten_hierarchy : 1;
|
||||
unsigned int visible_layers_only : 1;
|
||||
unsigned int renderable_only : 1;
|
||||
unsigned int face_sets : 1;
|
||||
unsigned int use_subdiv_schema : 1;
|
||||
unsigned int packuv : 1;
|
||||
unsigned int triangulate : 1;
|
||||
bool selected_only;
|
||||
bool uvs;
|
||||
bool normals;
|
||||
bool vcolors;
|
||||
bool apply_subdiv;
|
||||
bool flatten_hierarchy;
|
||||
bool visible_layers_only;
|
||||
bool renderable_only;
|
||||
bool face_sets;
|
||||
bool use_subdiv_schema;
|
||||
bool packuv;
|
||||
bool triangulate;
|
||||
bool export_hair;
|
||||
bool export_particles;
|
||||
|
||||
unsigned int compression_type : 1;
|
||||
|
||||
/* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx
|
||||
* in DNA_modifier_types.h */
|
||||
int quad_method;
|
||||
int ngon_method;
|
||||
|
||||
float global_scale;
|
||||
};
|
||||
|
||||
void ABC_export(
|
||||
/* The ABC_export and ABC_import functions both take a as_background_job
|
||||
* parameter, and return a boolean.
|
||||
*
|
||||
* When as_background_job=true, returns false immediately after scheduling
|
||||
* a background job.
|
||||
*
|
||||
* When as_background_job=false, performs the export synchronously, and returns
|
||||
* true when the export was ok, and false if there were any errors.
|
||||
*/
|
||||
|
||||
bool ABC_export(
|
||||
struct Scene *scene,
|
||||
struct bContext *C,
|
||||
const char *filepath,
|
||||
const struct AlembicExportParams *params);
|
||||
const struct AlembicExportParams *params,
|
||||
bool as_background_job);
|
||||
|
||||
void ABC_import(struct bContext *C,
|
||||
bool ABC_import(struct bContext *C,
|
||||
const char *filepath,
|
||||
float scale,
|
||||
bool is_sequence,
|
||||
bool set_frame_range,
|
||||
int sequence_len,
|
||||
int offset,
|
||||
bool validate_meshes);
|
||||
bool validate_meshes,
|
||||
bool as_background_job);
|
||||
|
||||
AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths);
|
||||
|
||||
|
|
|
@ -217,16 +217,17 @@ void AbcCurveReader::readObjectData(Main *bmain, float time)
|
|||
|
||||
cu->flag |= CU_DEFORM_FILL | CU_3D;
|
||||
cu->actvert = CU_ACT_NONE;
|
||||
cu->resolu = 1;
|
||||
|
||||
const ISampleSelector sample_sel(time);
|
||||
|
||||
ICompoundProperty user_props = m_curves_schema.getUserProperties();
|
||||
if (user_props) {
|
||||
const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME);
|
||||
if (header != NULL && header->isScalar() && IInt16Property::matches(*header)) {
|
||||
IInt16Property resolu(user_props, header->getName());
|
||||
cu->resolu = resolu.getValue(sample_sel);
|
||||
}
|
||||
else {
|
||||
cu->resolu = 1;
|
||||
}
|
||||
|
||||
m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str());
|
||||
|
|
|
@ -83,6 +83,8 @@ ExportSettings::ExportSettings()
|
|||
, export_vcols(false)
|
||||
, export_face_sets(false)
|
||||
, export_vweigths(false)
|
||||
, export_hair(true)
|
||||
, export_particles(true)
|
||||
, apply_subdiv(false)
|
||||
, use_subdiv_schema(false)
|
||||
, export_child_hairs(true)
|
||||
|
@ -528,6 +530,29 @@ void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Base *ob_base, Obje
|
|||
free_object_duplilist(lb);
|
||||
}
|
||||
|
||||
void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform)
|
||||
{
|
||||
if (!m_settings.export_hair && !m_settings.export_particles) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
|
||||
|
||||
for (; psys; psys = psys->next) {
|
||||
if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_settings.export_hair && psys->part->type == PART_HAIR) {
|
||||
m_settings.export_child_hairs = true;
|
||||
m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
|
||||
}
|
||||
else if (m_settings.export_particles && psys->part->type == PART_EMITTER) {
|
||||
m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AbcExporter::createShapeWriter(Base *ob_base, Object *dupliObParent)
|
||||
{
|
||||
Object *ob = ob_base->object;
|
||||
|
@ -552,21 +577,7 @@ void AbcExporter::createShapeWriter(Base *ob_base, Object *dupliObParent)
|
|||
return;
|
||||
}
|
||||
|
||||
ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
|
||||
|
||||
for (; psys; psys = psys->next) {
|
||||
if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (psys->part->type == PART_HAIR) {
|
||||
m_settings.export_child_hairs = true;
|
||||
m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
|
||||
}
|
||||
else if (psys->part->type == PART_EMITTER) {
|
||||
m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
|
||||
}
|
||||
}
|
||||
createParticleSystemsWriters(ob, xform);
|
||||
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
|
|
|
@ -66,6 +66,8 @@ struct ExportSettings {
|
|||
bool export_vcols;
|
||||
bool export_face_sets;
|
||||
bool export_vweigths;
|
||||
bool export_hair;
|
||||
bool export_particles;
|
||||
|
||||
bool apply_subdiv;
|
||||
bool use_subdiv_schema;
|
||||
|
@ -117,6 +119,7 @@ private:
|
|||
void exploreObject(EvaluationContext *eval_ctx, Base *ob_base, Object *dupliObParent);
|
||||
void createShapeWriters(EvaluationContext *eval_ctx);
|
||||
void createShapeWriter(Base *ob_base, Object *dupliObParent);
|
||||
void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform);
|
||||
|
||||
AbcTransformWriter *getXForm(const std::string &name);
|
||||
|
||||
|
|
|
@ -240,6 +240,7 @@ struct ExportJobData {
|
|||
float *progress;
|
||||
|
||||
bool was_canceled;
|
||||
bool export_ok;
|
||||
};
|
||||
|
||||
static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
|
||||
|
@ -272,6 +273,8 @@ static void export_startjob(void *customdata, short *stop, short *do_update, flo
|
|||
|
||||
BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain, scene);
|
||||
}
|
||||
|
||||
data->export_ok = !data->was_canceled;
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n';
|
||||
|
@ -298,15 +301,17 @@ static void export_endjob(void *customdata)
|
|||
BKE_spacedata_draw_locks(false);
|
||||
}
|
||||
|
||||
void ABC_export(
|
||||
bool ABC_export(
|
||||
Scene *scene,
|
||||
bContext *C,
|
||||
const char *filepath,
|
||||
const struct AlembicExportParams *params)
|
||||
const struct AlembicExportParams *params,
|
||||
bool as_background_job)
|
||||
{
|
||||
ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
|
||||
job->scene = scene;
|
||||
job->bmain = CTX_data_main(C);
|
||||
job->export_ok = false;
|
||||
BLI_strncpy(job->filename, filepath, 1024);
|
||||
|
||||
/* Alright, alright, alright....
|
||||
|
@ -348,6 +353,8 @@ void ABC_export(
|
|||
job->settings.export_normals = params->normals;
|
||||
job->settings.export_uvs = params->uvs;
|
||||
job->settings.export_vcols = params->vcolors;
|
||||
job->settings.export_hair = params->export_hair;
|
||||
job->settings.export_particles = params->export_particles;
|
||||
job->settings.apply_subdiv = params->apply_subdiv;
|
||||
job->settings.flatten_hierarchy = params->flatten_hierarchy;
|
||||
|
||||
|
@ -369,6 +376,7 @@ void ABC_export(
|
|||
std::swap(job->settings.frame_start, job->settings.frame_end);
|
||||
}
|
||||
|
||||
if (as_background_job) {
|
||||
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
|
||||
CTX_wm_window(C),
|
||||
job->scene,
|
||||
|
@ -383,6 +391,17 @@ void ABC_export(
|
|||
|
||||
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
||||
}
|
||||
else {
|
||||
/* Fake a job context, so that we don't need NULL pointer checks while exporting. */
|
||||
short stop = 0, do_update = 0;
|
||||
float progress = 0.f;
|
||||
|
||||
export_startjob(job, &stop, &do_update, &progress);
|
||||
export_endjob(job);
|
||||
}
|
||||
|
||||
return job->export_ok;
|
||||
}
|
||||
|
||||
/* ********************** Import file ********************** */
|
||||
|
||||
|
@ -610,6 +629,7 @@ struct ImportJobData {
|
|||
|
||||
char error_code;
|
||||
bool was_cancelled;
|
||||
bool import_ok;
|
||||
};
|
||||
|
||||
ABC_INLINE bool is_mesh_and_strands(const IObject &object)
|
||||
|
@ -818,6 +838,7 @@ static void import_endjob(void *user_data)
|
|||
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.");
|
||||
|
@ -833,13 +854,16 @@ static void import_freejob(void *user_data)
|
|||
delete data;
|
||||
}
|
||||
|
||||
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)
|
||||
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_* funcs do not call ctor to properly initialize
|
||||
* data. */
|
||||
ImportJobData *job = new ImportJobData();
|
||||
job->bmain = CTX_data_main(C);
|
||||
job->scene = CTX_data_scene(C);
|
||||
job->import_ok = false;
|
||||
BLI_strncpy(job->filename, filepath, 1024);
|
||||
|
||||
job->settings.scale = scale;
|
||||
|
@ -853,6 +877,7 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence
|
|||
|
||||
G.is_break = false;
|
||||
|
||||
if (as_background_job) {
|
||||
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
|
||||
CTX_wm_window(C),
|
||||
job->scene,
|
||||
|
@ -867,6 +892,17 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence
|
|||
|
||||
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.f;
|
||||
|
||||
import_startjob(job, &stop, &do_update, &progress);
|
||||
import_endjob(job);
|
||||
}
|
||||
|
||||
return job->import_ok;
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
|
|
|
@ -122,6 +122,8 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
|
|||
.renderable_only = RNA_boolean_get(op->ptr, "renderable_only"),
|
||||
.face_sets = RNA_boolean_get(op->ptr, "face_sets"),
|
||||
.use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"),
|
||||
.export_hair = RNA_boolean_get(op->ptr, "export_hair"),
|
||||
.export_particles = RNA_boolean_get(op->ptr, "export_particles"),
|
||||
.compression_type = RNA_enum_get(op->ptr, "compression_type"),
|
||||
.packuv = RNA_boolean_get(op->ptr, "packuv"),
|
||||
.triangulate = RNA_boolean_get(op->ptr, "triangulate"),
|
||||
|
@ -131,15 +133,17 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
|
|||
.global_scale = RNA_float_get(op->ptr, "global_scale"),
|
||||
};
|
||||
|
||||
ABC_export(CTX_data_scene(C), C, filename, ¶ms);
|
||||
const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job");
|
||||
bool ok = ABC_export(CTX_data_scene(C), C, filename, ¶ms, as_background_job);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
||||
{
|
||||
uiLayout *box;
|
||||
uiLayout *row;
|
||||
uiLayout *col;
|
||||
|
||||
#ifdef WITH_ALEMBIC_HDF5
|
||||
box = uiLayoutBox(layout);
|
||||
|
@ -231,6 +235,15 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
|||
row = uiLayoutRow(box, false);
|
||||
uiLayoutSetEnabled(row, triangulate);
|
||||
uiItemR(row, imfptr, "ngon_method", 0, NULL, ICON_NONE);
|
||||
|
||||
/* Object Data */
|
||||
box = uiLayoutBox(layout);
|
||||
row = uiLayoutRow(box, false);
|
||||
uiItemL(row, IFACE_("Particle Systems:"), ICON_PARTICLE_DATA);
|
||||
|
||||
col = uiLayoutColumn(box, true);
|
||||
uiItemR(col, imfptr, "export_hair", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, imfptr, "export_particles", 0, NULL, ICON_NONE);
|
||||
}
|
||||
|
||||
static void wm_alembic_export_draw(bContext *C, wmOperator *op)
|
||||
|
@ -348,6 +361,12 @@ void WM_OT_alembic_export(wmOperatorType *ot)
|
|||
RNA_def_enum(ot->srna, "ngon_method", rna_enum_modifier_triangulate_quad_method_items,
|
||||
MOD_TRIANGULATE_NGON_BEAUTY, "Polygon Method", "Method for splitting the polygons into triangles");
|
||||
|
||||
RNA_def_boolean(ot->srna, "export_hair", 1, "Export Hair", "Exports hair particle systems as animated curves");
|
||||
RNA_def_boolean(ot->srna, "export_particles", 1, "Export Particles", "Exports non-hair particle systems");
|
||||
|
||||
RNA_def_boolean(ot->srna, "as_background_job", true, "Run as Background Job",
|
||||
"Enable this to run the import in the background, disable to block Blender while importing");
|
||||
|
||||
/* This dummy prop is used to check whether we need to init the start and
|
||||
* end frame values to that of the scene's, otherwise they are reset at
|
||||
* every change, draw update. */
|
||||
|
@ -482,6 +501,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op)
|
|||
const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence");
|
||||
const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range");
|
||||
const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
|
||||
const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job");
|
||||
|
||||
int offset = 0;
|
||||
int sequence_len = 1;
|
||||
|
@ -490,9 +510,11 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op)
|
|||
sequence_len = get_sequence_len(filename, &offset);
|
||||
}
|
||||
|
||||
ABC_import(C, filename, scale, is_sequence, set_frame_range, sequence_len, offset, validate_meshes);
|
||||
bool ok = ABC_import(C, filename, scale, is_sequence, set_frame_range,
|
||||
sequence_len, offset, validate_meshes,
|
||||
as_background_job);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void WM_OT_alembic_import(wmOperatorType *ot)
|
||||
|
@ -523,6 +545,9 @@ void WM_OT_alembic_import(wmOperatorType *ot)
|
|||
|
||||
RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence",
|
||||
"Set to true if the cache is split into separate files");
|
||||
|
||||
RNA_def_boolean(ot->srna, "as_background_job", true, "Run as Background Job",
|
||||
"Enable this to run the export in the background, disable to block Blender while exporting");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -207,6 +207,8 @@ static void rna_Scene_alembic_export(
|
|||
int renderable_only,
|
||||
int face_sets,
|
||||
int use_subdiv_schema,
|
||||
int export_hair,
|
||||
int export_particles,
|
||||
int compression_type,
|
||||
int packuv,
|
||||
float scale,
|
||||
|
@ -240,6 +242,8 @@ static void rna_Scene_alembic_export(
|
|||
.renderable_only = renderable_only,
|
||||
.face_sets = face_sets,
|
||||
.use_subdiv_schema = use_subdiv_schema,
|
||||
.export_hair = export_hair,
|
||||
.export_particles = export_particles,
|
||||
.compression_type = compression_type,
|
||||
.packuv = packuv,
|
||||
.triangulate = triangulate,
|
||||
|
@ -249,7 +253,7 @@ static void rna_Scene_alembic_export(
|
|||
.global_scale = scale,
|
||||
};
|
||||
|
||||
ABC_export(scene, C, filepath, ¶ms);
|
||||
ABC_export(scene, C, filepath, ¶ms, true);
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_END_ALLOW_THREADS;
|
||||
|
@ -440,8 +444,9 @@ void RNA_api_scene(StructRNA *srna)
|
|||
#endif
|
||||
|
||||
#ifdef WITH_ALEMBIC
|
||||
/* XXX Deprecated, will be removed in 2.8 in favour of calling the export operator. */
|
||||
func = RNA_def_function(srna, "alembic_export", "rna_Scene_alembic_export");
|
||||
RNA_def_function_ui_description(func, "Export to Alembic file");
|
||||
RNA_def_function_ui_description(func, "Export to Alembic file (deprecated, use the Alembic export operator)");
|
||||
|
||||
parm = RNA_def_string(func, "filepath", NULL, FILE_MAX, "File Path", "File path to write Alembic file");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
|
@ -463,6 +468,8 @@ void RNA_api_scene(StructRNA *srna)
|
|||
RNA_def_boolean(func, "renderable_only" , 0, "Renderable objects only", "Export only objects marked renderable in the outliner");
|
||||
RNA_def_boolean(func, "face_sets" , 0, "Facesets", "Export face sets");
|
||||
RNA_def_boolean(func, "subdiv_schema", 0, "Use Alembic subdivision Schema", "Use Alembic subdivision Schema");
|
||||
RNA_def_boolean(func, "export_hair", 1, "Export Hair", "Exports hair particle systems as animated curves");
|
||||
RNA_def_boolean(func, "export_particles", 1, "Export Particles", "Exports non-hair particle systems");
|
||||
RNA_def_enum(func, "compression_type", rna_enum_abc_compression_items, 0, "Compression", "");
|
||||
RNA_def_boolean(func, "packuv" , 0, "Export with packed UV islands", "Export with packed UV islands");
|
||||
RNA_def_float(func, "scale", 1.0f, 0.0001f, 1000.0f, "Scale", "Value by which to enlarge or shrink the objects with respect to the world's origin", 0.0001f, 1000.0f);
|
||||
|
|
|
@ -54,6 +54,10 @@ def with_tempdir(wrapped):
|
|||
return decorator
|
||||
|
||||
|
||||
class AbcPropError(Exception):
|
||||
"""Raised when AbstractAlembicTest.abcprop() finds an error."""
|
||||
|
||||
|
||||
class AbstractAlembicTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
@ -104,8 +108,7 @@ class AbstractAlembicTest(unittest.TestCase):
|
|||
def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict:
|
||||
"""Uses abcls to obtain compound property values from an Alembic object.
|
||||
|
||||
A dict of subproperties is returned, where the values are just strings
|
||||
as returned by abcls.
|
||||
A dict of subproperties is returned, where the values are Python values.
|
||||
|
||||
The Python bindings for Alembic are old, and only compatible with Python 2.x,
|
||||
so that's why we can't use them here, and have to rely on other tooling.
|
||||
|
@ -122,7 +125,7 @@ class AbstractAlembicTest(unittest.TestCase):
|
|||
output = self.ansi_remove_re.sub(b'', coloured_output).decode('utf8')
|
||||
|
||||
if proc.returncode:
|
||||
self.fail('Error %d running abcls:\n%s' % (proc.returncode, output))
|
||||
raise AbcPropError('Error %d running abcls:\n%s' % (proc.returncode, output))
|
||||
|
||||
# Mapping from value type to callable that can convert a string to Python values.
|
||||
converters = {
|
||||
|
@ -130,6 +133,7 @@ class AbstractAlembicTest(unittest.TestCase):
|
|||
'uint8_t': int,
|
||||
'int16_t': int,
|
||||
'int32_t': int,
|
||||
'uint64_t': int,
|
||||
'float64_t': float,
|
||||
'float32_t': float,
|
||||
}
|
||||
|
@ -302,6 +306,72 @@ class CurveExportTest(AbstractAlembicTest):
|
|||
self.assertEqual(abcprop['blender:resolution'], 10)
|
||||
|
||||
|
||||
class HairParticlesExportTest(AbstractAlembicTest):
|
||||
"""Tests exporting with/without hair/particles.
|
||||
|
||||
Just a basic test to ensure that the enabling/disabling works, and that export
|
||||
works at all. NOT testing the quality/contents of the exported file.
|
||||
"""
|
||||
|
||||
def _do_test(self, tempdir: pathlib.Path, export_hair: bool, export_particles: bool) -> pathlib.Path:
|
||||
abc = tempdir / 'hair-particles.abc'
|
||||
script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
|
||||
"renderable_only=True, visible_layers_only=True, flatten=False, " \
|
||||
"export_hair=%r, export_particles=%r, as_background_job=False)" \
|
||||
% (abc, export_hair, export_particles)
|
||||
self.run_blender('hair-particles.blend', script)
|
||||
return abc
|
||||
|
||||
@with_tempdir
|
||||
def test_with_both(self, tempdir: pathlib.Path):
|
||||
abc = self._do_test(tempdir, True, True)
|
||||
|
||||
abcprop = self.abcprop(abc, '/Suzanne/Hair system/.geom')
|
||||
self.assertIn('nVertices', abcprop)
|
||||
|
||||
abcprop = self.abcprop(abc, '/Suzanne/Non-hair particle system/.geom')
|
||||
self.assertIn('.velocities', abcprop)
|
||||
|
||||
abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
|
||||
self.assertIn('.faceIndices', abcprop)
|
||||
|
||||
@with_tempdir
|
||||
def test_with_hair_only(self, tempdir: pathlib.Path):
|
||||
abc = self._do_test(tempdir, True, False)
|
||||
|
||||
abcprop = self.abcprop(abc, '/Suzanne/Hair system/.geom')
|
||||
self.assertIn('nVertices', abcprop)
|
||||
|
||||
self.assertRaises(AbcPropError, self.abcprop, abc,
|
||||
'/Suzanne/Non-hair particle system/.geom')
|
||||
|
||||
abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
|
||||
self.assertIn('.faceIndices', abcprop)
|
||||
|
||||
@with_tempdir
|
||||
def test_with_particles_only(self, tempdir: pathlib.Path):
|
||||
abc = self._do_test(tempdir, False, True)
|
||||
|
||||
self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair system/.geom')
|
||||
|
||||
abcprop = self.abcprop(abc, '/Suzanne/Non-hair particle system/.geom')
|
||||
self.assertIn('.velocities', abcprop)
|
||||
|
||||
abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
|
||||
self.assertIn('.faceIndices', abcprop)
|
||||
|
||||
@with_tempdir
|
||||
def test_with_neither(self, tempdir: pathlib.Path):
|
||||
abc = self._do_test(tempdir, False, False)
|
||||
|
||||
self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair system/.geom')
|
||||
self.assertRaises(AbcPropError, self.abcprop, abc,
|
||||
'/Suzanne/Non-hair particle system/.geom')
|
||||
|
||||
abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
|
||||
self.assertIn('.faceIndices', abcprop)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--blender', required=True)
|
||||
|
|
Loading…
Reference in New Issue