Alembic export: made hair/particle export optional.
The export is still slower than needed, as the particle systems themselves aren't disabled during the export. It's only the writing to the Alembic file that's skipped.
This commit is contained in:
parent
0fd53c87de
commit
b148ac5cf7
|
@ -65,6 +65,8 @@ struct AlembicExportParams {
|
|||
bool use_subdiv_schema;
|
||||
bool packuv;
|
||||
bool triangulate;
|
||||
bool export_hair;
|
||||
bool export_particles;
|
||||
|
||||
unsigned int compression_type : 1;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
@ -525,6 +527,29 @@ void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Object *ob, Object
|
|||
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(Object *ob, Object *dupliObParent)
|
||||
{
|
||||
if (!object_is_shape(ob)) {
|
||||
|
@ -547,21 +572,7 @@ void AbcExporter::createShapeWriter(Object *ob, 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:
|
||||
|
|
|
@ -63,6 +63,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;
|
||||
|
@ -114,6 +116,7 @@ private:
|
|||
void exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent);
|
||||
void createShapeWriters(EvaluationContext *eval_ctx);
|
||||
void createShapeWriter(Object *ob, Object *dupliObParent);
|
||||
void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform);
|
||||
|
||||
AbcTransformWriter *getXForm(const std::string &name);
|
||||
|
||||
|
|
|
@ -336,6 +336,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;
|
||||
job->settings.visible_layers_only = params->visible_layers_only;
|
||||
|
|
|
@ -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"),
|
||||
|
@ -140,6 +142,7 @@ 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 +234,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 +360,9 @@ 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");
|
||||
|
||||
/* 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. */
|
||||
|
|
|
@ -208,6 +208,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,
|
||||
|
@ -241,6 +243,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,
|
||||
|
@ -458,6 +462,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,71 @@ 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)" % (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