Alembic export: fixed curve type and order.
authorSybren A. Stüvel <sybren@stuvel.eu>
Tue, 18 Apr 2017 10:17:07 +0000 (12:17 +0200)
committerSybren A. Stüvel <sybren@stuvel.eu>
Tue, 18 Apr 2017 11:57:04 +0000 (13:57 +0200)
The order number written to Alembic is the same as we use in memory, so
the +1 wasn't needed, at least according to the reference Maya exporter
maya/AbcExport/MayaNurbsCurveWriter.cpp, function
MayaNurbsCurveWriter::write(), in the Alembic source code.

Furthermore, when writing an array of nurb orders, the curve type should
be set to kVariableOrder, otherwise the importer will ignore it.

source/blender/alembic/intern/abc_curves.cc
tests/python/alembic_tests.py

index 28e75db2862538006cdd4018993dc1af3f1035c2..58b8d7e05cd03838b8095c07f13954a282d0a5d9 100644 (file)
@@ -95,7 +95,7 @@ void AbcCurveWriter::do_write()
        for (; nurbs; nurbs = nurbs->next) {
                if (nurbs->bp) {
                        curve_basis = Alembic::AbcGeom::kNoBasis;
-                       curve_type = Alembic::AbcGeom::kLinear;
+                       curve_type = Alembic::AbcGeom::kVariableOrder;
 
                        const int totpoint = nurbs->pntsu * nurbs->pntsv;
 
@@ -160,7 +160,7 @@ void AbcCurveWriter::do_write()
                        }
                }
 
-               orders.push_back(nurbs->orderu + 1);
+               orders.push_back(nurbs->orderu);
                vert_counts.push_back(verts.size());
        }
 
index 209b34a8634cc1b0bc12f0fd3f13cefcd81237cd..14c817549a9ae27b0e2fc7dd5ac54e5287da71ca 100755 (executable)
@@ -116,6 +116,7 @@ class AbstractAlembicTest(unittest.TestCase):
         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.
         """
+        import collections
 
         abcls = self.alembic_root / 'bin' / 'abcls'
 
@@ -133,14 +134,19 @@ class AbstractAlembicTest(unittest.TestCase):
         converters = {
             'bool_t': int,
             'uint8_t': int,
+            'int32_t': int,
             'float64_t': float,
+            'float32_t': float,
         }
 
         result = {}
 
         # Ideally we'd get abcls to output JSON, see https://github.com/alembic/alembic/issues/121
-        lines = output.split('\n')
-        for info, value in zip(lines[0::2], lines[1::2]):
+        lines = collections.deque(output.split('\n'))
+        while lines:
+            info = lines.popleft()
+            if not info:
+                continue
             proptype, valtype_and_arrsize, name_and_extent = info.split()
 
             # Parse name and extent
@@ -152,22 +158,41 @@ class AbstractAlembicTest(unittest.TestCase):
             if extent != '1':
                 self.fail('Unsupported extent %s for property %s/%s' % (extent, proppath, name))
 
-            # Parse type and convert values
+            # Parse type
             m = self.abcls_array.match(valtype_and_arrsize)
             if not m:
                 self.fail('Unparsable value type from abcls: %s' % valtype_and_arrsize)
-            valtype, arraysize = m.group('name'), m.group('arraysize')
+            valtype, scalarsize = m.group('name'), m.group('arraysize')
 
+            # Convert values
             try:
                 conv = converters[valtype]
             except KeyError:
                 self.fail('Unsupported type %s for property %s/%s' % (valtype, proppath, name))
 
-            if arraysize is None:
-                result[name] = conv(value)
+            def convert_single_line(linevalue):
+                try:
+                    if scalarsize is None:
+                        return conv(linevalue)
+                    else:
+                        return [conv(v.strip()) for v in linevalue.split(',')]
+                except ValueError as ex:
+                    return str(ex)
+
+            if proptype == 'ScalarProperty':
+                value = lines.popleft()
+                result[name] = convert_single_line(value)
+            elif proptype == 'ArrayProperty':
+                arrayvalue = []
+                # Arrays consist of a variable number of items, and end in a blank line.
+                while True:
+                    linevalue = lines.popleft()
+                    if not linevalue:
+                        break
+                    arrayvalue.append(convert_single_line(linevalue))
+                result[name] = arrayvalue
             else:
-                values = [conv(v.strip()) for v in value.split(',')]
-                result[name] = values
+                self.fail('Unsupported type %s for property %s/%s' % (proptype, proppath, name))
 
         return result
 
@@ -260,6 +285,17 @@ class DupliGroupExportTest(AbstractAlembicTest):
         )
 
 
+class CurveExportTest(AbstractAlembicTest):
+    @with_tempdir
+    def test_export_single_curve(self, tempdir: pathlib.Path):
+        abc = tempdir / 'single-curve.abc'
+        script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
+                 "renderable_only=True, visible_layers_only=True, flatten=False)" % abc
+        self.run_blender('single-curve.blend', script)
+
+        # Now check the resulting Alembic file.
+        abcprop = self.abcprop(abc, '/NurbsCurve/NurbsCurveShape/.geom')
+        self.assertEqual(abcprop['.orders'], [4])
 
 
 if __name__ == '__main__':