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:
2017-04-19 15:07:54 +02:00
9 changed files with 244 additions and 76 deletions

View File

@@ -176,11 +176,10 @@ def draw(layout, context, context_member, property_type, use_edit=True):
if not is_rna: if not is_rna:
props = row.operator("wm.properties_edit", text="Edit") props = row.operator("wm.properties_edit", text="Edit")
assign_props(props, val_draw, key) assign_props(props, val_draw, key)
else:
row.label(text="API Defined")
props = row.operator("wm.properties_remove", text="", icon='ZOOMOUT') props = row.operator("wm.properties_remove", text="", icon='ZOOMOUT')
assign_props(props, val_draw, key) assign_props(props, val_draw, key)
else:
row.label(text="API Defined")
class PropertyPanel: class PropertyPanel:

View File

@@ -53,41 +53,57 @@ struct AlembicExportParams {
double shutter_open; double shutter_open;
double shutter_close; double shutter_close;
/* bools */ bool selected_only;
unsigned int selected_only : 1; bool uvs;
unsigned int uvs : 1; bool normals;
unsigned int normals : 1; bool vcolors;
unsigned int vcolors : 1; bool apply_subdiv;
unsigned int apply_subdiv : 1; bool flatten_hierarchy;
unsigned int flatten_hierarchy : 1; bool visible_layers_only;
unsigned int visible_layers_only : 1; bool renderable_only;
unsigned int renderable_only : 1; bool face_sets;
unsigned int face_sets : 1; bool use_subdiv_schema;
unsigned int use_subdiv_schema : 1; bool packuv;
unsigned int packuv : 1; bool triangulate;
unsigned int triangulate : 1; bool export_hair;
bool export_particles;
unsigned int compression_type : 1; unsigned int compression_type : 1;
/* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx
* in DNA_modifier_types.h */
int quad_method; int quad_method;
int ngon_method; int ngon_method;
float global_scale; 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 Scene *scene,
struct bContext *C, struct bContext *C,
const char *filepath, 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, const char *filepath,
float scale, float scale,
bool is_sequence, bool is_sequence,
bool set_frame_range, bool set_frame_range,
int sequence_len, int sequence_len,
int offset, int offset,
bool validate_meshes); bool validate_meshes,
bool as_background_job);
AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths); AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths);

View File

@@ -217,16 +217,17 @@ void AbcCurveReader::readObjectData(Main *bmain, float time)
cu->flag |= CU_DEFORM_FILL | CU_3D; cu->flag |= CU_DEFORM_FILL | CU_3D;
cu->actvert = CU_ACT_NONE; cu->actvert = CU_ACT_NONE;
cu->resolu = 1;
const ISampleSelector sample_sel(time); const ISampleSelector sample_sel(time);
ICompoundProperty user_props = m_curves_schema.getUserProperties(); ICompoundProperty user_props = m_curves_schema.getUserProperties();
if (user_props) {
const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME); const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME);
if (header != NULL && header->isScalar() && IInt16Property::matches(*header)) { if (header != NULL && header->isScalar() && IInt16Property::matches(*header)) {
IInt16Property resolu(user_props, header->getName()); IInt16Property resolu(user_props, header->getName());
cu->resolu = resolu.getValue(sample_sel); 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()); m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str());

View File

@@ -83,6 +83,8 @@ ExportSettings::ExportSettings()
, export_vcols(false) , export_vcols(false)
, export_face_sets(false) , export_face_sets(false)
, export_vweigths(false) , export_vweigths(false)
, export_hair(true)
, export_particles(true)
, apply_subdiv(false) , apply_subdiv(false)
, use_subdiv_schema(false) , use_subdiv_schema(false)
, export_child_hairs(true) , export_child_hairs(true)
@@ -528,6 +530,29 @@ void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Base *ob_base, Obje
free_object_duplilist(lb); 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) void AbcExporter::createShapeWriter(Base *ob_base, Object *dupliObParent)
{ {
Object *ob = ob_base->object; Object *ob = ob_base->object;
@@ -552,21 +577,7 @@ void AbcExporter::createShapeWriter(Base *ob_base, Object *dupliObParent)
return; return;
} }
ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first); createParticleSystemsWriters(ob, xform);
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));
}
}
switch (ob->type) { switch (ob->type) {
case OB_MESH: case OB_MESH:

View File

@@ -66,6 +66,8 @@ struct ExportSettings {
bool export_vcols; bool export_vcols;
bool export_face_sets; bool export_face_sets;
bool export_vweigths; bool export_vweigths;
bool export_hair;
bool export_particles;
bool apply_subdiv; bool apply_subdiv;
bool use_subdiv_schema; bool use_subdiv_schema;
@@ -117,6 +119,7 @@ private:
void exploreObject(EvaluationContext *eval_ctx, Base *ob_base, Object *dupliObParent); void exploreObject(EvaluationContext *eval_ctx, Base *ob_base, Object *dupliObParent);
void createShapeWriters(EvaluationContext *eval_ctx); void createShapeWriters(EvaluationContext *eval_ctx);
void createShapeWriter(Base *ob_base, Object *dupliObParent); void createShapeWriter(Base *ob_base, Object *dupliObParent);
void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform);
AbcTransformWriter *getXForm(const std::string &name); AbcTransformWriter *getXForm(const std::string &name);

View File

@@ -240,6 +240,7 @@ struct ExportJobData {
float *progress; float *progress;
bool was_canceled; bool was_canceled;
bool export_ok;
}; };
static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) 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); BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain, scene);
} }
data->export_ok = !data->was_canceled;
} }
catch (const std::exception &e) { catch (const std::exception &e) {
ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; 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); BKE_spacedata_draw_locks(false);
} }
void ABC_export( bool ABC_export(
Scene *scene, Scene *scene,
bContext *C, bContext *C,
const char *filepath, 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")); ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
job->scene = scene; job->scene = scene;
job->bmain = CTX_data_main(C); job->bmain = CTX_data_main(C);
job->export_ok = false;
BLI_strncpy(job->filename, filepath, 1024); BLI_strncpy(job->filename, filepath, 1024);
/* Alright, alright, alright.... /* Alright, alright, alright....
@@ -348,6 +353,8 @@ void ABC_export(
job->settings.export_normals = params->normals; job->settings.export_normals = params->normals;
job->settings.export_uvs = params->uvs; job->settings.export_uvs = params->uvs;
job->settings.export_vcols = params->vcolors; 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.apply_subdiv = params->apply_subdiv;
job->settings.flatten_hierarchy = params->flatten_hierarchy; job->settings.flatten_hierarchy = params->flatten_hierarchy;
@@ -369,6 +376,7 @@ void ABC_export(
std::swap(job->settings.frame_start, job->settings.frame_end); std::swap(job->settings.frame_start, job->settings.frame_end);
} }
if (as_background_job) {
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,
@@ -382,6 +390,17 @@ void ABC_export(
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);
}
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 ********************** */ /* ********************** Import file ********************** */
@@ -610,6 +629,7 @@ struct ImportJobData {
char error_code; char error_code;
bool was_cancelled; bool was_cancelled;
bool import_ok;
}; };
ABC_INLINE bool is_mesh_and_strands(const IObject &object) 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) { switch (data->error_code) {
default: default:
case ABC_NO_ERROR: case ABC_NO_ERROR:
data->import_ok = !data->was_cancelled;
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.");
@@ -833,13 +854,16 @@ static void import_freejob(void *user_data)
delete 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 /* Using new here since MEM_* funcs do not call ctor to properly initialize
* data. */ * data. */
ImportJobData *job = new ImportJobData(); ImportJobData *job = new ImportJobData();
job->bmain = CTX_data_main(C); job->bmain = CTX_data_main(C);
job->scene = CTX_data_scene(C); job->scene = CTX_data_scene(C);
job->import_ok = false;
BLI_strncpy(job->filename, filepath, 1024); BLI_strncpy(job->filename, filepath, 1024);
job->settings.scale = scale; 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; G.is_break = false;
if (as_background_job) {
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,
@@ -866,6 +891,17 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence
WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob); WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob);
WM_jobs_start(CTX_wm_manager(C), wm_job); 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;
} }
/* ************************************************************************** */ /* ************************************************************************** */

View File

@@ -122,6 +122,8 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
.renderable_only = RNA_boolean_get(op->ptr, "renderable_only"), .renderable_only = RNA_boolean_get(op->ptr, "renderable_only"),
.face_sets = RNA_boolean_get(op->ptr, "face_sets"), .face_sets = RNA_boolean_get(op->ptr, "face_sets"),
.use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"), .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"), .compression_type = RNA_enum_get(op->ptr, "compression_type"),
.packuv = RNA_boolean_get(op->ptr, "packuv"), .packuv = RNA_boolean_get(op->ptr, "packuv"),
.triangulate = RNA_boolean_get(op->ptr, "triangulate"), .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"), .global_scale = RNA_float_get(op->ptr, "global_scale"),
}; };
ABC_export(CTX_data_scene(C), C, filename, &params); const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job");
bool ok = ABC_export(CTX_data_scene(C), C, filename, &params, 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) static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
{ {
uiLayout *box; uiLayout *box;
uiLayout *row; uiLayout *row;
uiLayout *col;
#ifdef WITH_ALEMBIC_HDF5 #ifdef WITH_ALEMBIC_HDF5
box = uiLayoutBox(layout); box = uiLayoutBox(layout);
@@ -231,6 +235,15 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
row = uiLayoutRow(box, false); row = uiLayoutRow(box, false);
uiLayoutSetEnabled(row, triangulate); uiLayoutSetEnabled(row, triangulate);
uiItemR(row, imfptr, "ngon_method", 0, NULL, ICON_NONE); 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) 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, 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"); 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 /* 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 * end frame values to that of the scene's, otherwise they are reset at
* every change, draw update. */ * 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 is_sequence = RNA_boolean_get(op->ptr, "is_sequence");
const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range"); 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 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 offset = 0;
int sequence_len = 1; 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); 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) 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", RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence",
"Set to true if the cache is split into separate files"); "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 #endif

View File

@@ -207,6 +207,8 @@ static void rna_Scene_alembic_export(
int renderable_only, int renderable_only,
int face_sets, int face_sets,
int use_subdiv_schema, int use_subdiv_schema,
int export_hair,
int export_particles,
int compression_type, int compression_type,
int packuv, int packuv,
float scale, float scale,
@@ -240,6 +242,8 @@ static void rna_Scene_alembic_export(
.renderable_only = renderable_only, .renderable_only = renderable_only,
.face_sets = face_sets, .face_sets = face_sets,
.use_subdiv_schema = use_subdiv_schema, .use_subdiv_schema = use_subdiv_schema,
.export_hair = export_hair,
.export_particles = export_particles,
.compression_type = compression_type, .compression_type = compression_type,
.packuv = packuv, .packuv = packuv,
.triangulate = triangulate, .triangulate = triangulate,
@@ -249,7 +253,7 @@ static void rna_Scene_alembic_export(
.global_scale = scale, .global_scale = scale,
}; };
ABC_export(scene, C, filepath, &params); ABC_export(scene, C, filepath, &params, true);
#ifdef WITH_PYTHON #ifdef WITH_PYTHON
BPy_END_ALLOW_THREADS; BPy_END_ALLOW_THREADS;
@@ -440,8 +444,9 @@ void RNA_api_scene(StructRNA *srna)
#endif #endif
#ifdef WITH_ALEMBIC #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"); 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"); 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); 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, "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, "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, "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_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_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); 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);

View File

@@ -54,6 +54,10 @@ def with_tempdir(wrapped):
return decorator return decorator
class AbcPropError(Exception):
"""Raised when AbstractAlembicTest.abcprop() finds an error."""
class AbstractAlembicTest(unittest.TestCase): class AbstractAlembicTest(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@@ -104,8 +108,7 @@ class AbstractAlembicTest(unittest.TestCase):
def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict: def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict:
"""Uses abcls to obtain compound property values from an Alembic object. """Uses abcls to obtain compound property values from an Alembic object.
A dict of subproperties is returned, where the values are just strings A dict of subproperties is returned, where the values are Python values.
as returned by abcls.
The Python bindings for Alembic are old, and only compatible with Python 2.x, 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. 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') output = self.ansi_remove_re.sub(b'', coloured_output).decode('utf8')
if proc.returncode: 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. # Mapping from value type to callable that can convert a string to Python values.
converters = { converters = {
@@ -130,6 +133,7 @@ class AbstractAlembicTest(unittest.TestCase):
'uint8_t': int, 'uint8_t': int,
'int16_t': int, 'int16_t': int,
'int32_t': int, 'int32_t': int,
'uint64_t': int,
'float64_t': float, 'float64_t': float,
'float32_t': float, 'float32_t': float,
} }
@@ -302,6 +306,72 @@ class CurveExportTest(AbstractAlembicTest):
self.assertEqual(abcprop['blender:resolution'], 10) 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__': if __name__ == '__main__':
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--blender', required=True) parser.add_argument('--blender', required=True)