Blender add-on to read and write Quill files
2024-06-15
14 minutes read

I’m writing Quill-Blender, a Blender add-on to provide import/export support of Quill files to Blender. It reads and writes native Quill files. Quill doesn’t need to be installed on the computer for it to work, it has no external dependencies.

Check out the project page on github.

demo Quill artwork imported in Blender with the add-on. Artwork by Gianpaol0

Itch to scratch

The goal of this add-on is to minimize the friction of moving between VR and 3D painting software, and help create workflows that best suit the strengths of each.

I’ve noticed that I tend to come up with over-complicated workflows trying to combine as many techniques as possible 😅. I find that using a particular combination of software in a way no one else has done before is already something interesting in itself, and that I’ll get something unique out of it by default.

The main art programs I use are Quill, Blender and Octane. Obviously with 3D workflows lots of time is spent exporting from one program and importing to another. Especially since my use of Octane is not standard, I use it for camera paths, generating geometry, scattering, etc., it’s not merely at the end of the chain, I can’t always use the Octane Blender add-on. There is a lot of back and forth between the programs.

The traditional way to get Quill stuff into a DCC is to export Alembic or FBX files from within Quill and import that into the other program. In the other direction there isn’t really anything to go from Blender to Quill in a satisfying way. The goal is to streamline the interchange as much as possible, especially between Quill and Grease Pencil.

basics

History

This is a rewrite and successor project to SharpQuill .NET library.

I’ve often thought about mixing Blender Grease Pencil and Quill, people create awesome art with the two and they seem like a match made in heaven. Both are based on 3D paint strokes, Quill having the most freedom, drawing in free space like spray painting in mid-air, and Blender having the most precision, with strokes that can stick to existing surfaces or planes, and can be used with a stylus on a graphics tablet.

But what actually sparked the project was armature based animation. I wanted to define and animate an armature in Blender and create the “skin” for it in Quill. The traditional workflow would be the opposite, paint the creature in Quill and create the rig for it from scratch, ugh… For my purpose it made more sense to start with the armature first.

insect-anim Quill strokes and anim-brush attached to a pre-existing armature

All that being said the data models of Quill and Blender are not at all perfect matches and the following paragraphs will go into more details about the differences.

Architecture

The add-on is relatively simple and organized as follows:

  • __init__.py: the main entry point. Registers the add-on and describes the UI for the import and export dialogs.
  • export_quill.py: scene-level export. Creates Quill layers, set up the transforms, and delegates to type-specific converters.
  • import_quill.py: scene-level import. Creates Blender objects, set up the transforms and delegates to type-specific converters.
  • exporters: type specific exporters.
    • paint_armature.py converts Blender armature to a Quill paint layer.
    • paint_gpencil.py converts Blender Grease pencil object to a Quill paint layer.
    • paint_wireframe.py converts Blender mesh to a Quill paint layer.
  • importers: type specific importers.
    • gpencil_paint.py converts Quill paint layer to Grease Pencil.
    • mesh_material.py helper functions to create the material for mesh import.
    • mesh_paint.py converts Quill paint layer to a Blender mesh.
  • model: low level Quill data model. Serialization from JSON and QBIN file formats to python objects and back.
    • paint.py: contains the serialization functions for the drawings data file, Quill.qbin.
    • sequence.py: contains the serialization functions for the scene file, Quill.json.
    • state.py: contains the serialization functions for the state file, State.json.

For some data types like groups or cameras the conversion is done directly in the import_quill.py or export_quill.py files.

Scene hierarchy

Quill’s scene graph is a hierarchy of layers, where the leaves of that tree are drawing sequences, cameras and viewpoints, or imported assets like audio, 3D models or images.

Blender can also organize the scene in a hierarchy of objects, but we hit the first major difference. The visibility of a particular layer in Quill depends on the visibility of the group it is in. Hiding the group hides all its content, which is logical. Blender objects don’t work like this, they only serve as a transformation parent.

Blender main other way to handle visibility is to put objects in collections and then add a collection “instance” to the hierarchy. But this doesn’t really work, if we have a deeply nested hierarchy of groups on the Quill side it becomes a complete mess.

This alone isn’t too much of an issue for non-animated layers. We can go through the child layers of hidden groups and hide everything. The add-on actually has an option to not import hidden layers in the first place.

The real problem is that Quill visibility model is way more sophisticated and driven by animation and the concept of sequences. Sequences let you treat everything inside them as a virtual clip and have it stop, restart or loop. Both the innermost clips and the sequences can loop at different points. I’ll have to revisit this topic in the future.

spans Quill advanced visibility model: key frames, loops, groups, sequences

In the remainder I’ll focus on Quill paint layers themselves rather than the scene hierarchy.

Importing Quill to Blender

When importing Quill paint layers to Blender there are two ways to go: tessellation (converting the paint strokes to mesh data) or Grease Pencil.

Tessellation

The first version of the add-on did not have any way to create mesh data from the paint strokes. After getting feedback from the first users it became clear that the Grease Pencil brush model was too different from Quill’s to represent scenes well enough to be useful in all cases.

I spent some time figuring out the exact way to go from Quill strokes to the corresponding meshes. Since the brush types are not radially symmetric it is crucial to get the correct orientation of the stroke at every point. The full basis can be reconstructed with the normal attribute and the location of the previous and next points. A C++ implementation can be found in the IMM repository.

tessellation Wrong tessellation (yellow) compared with reference alembic export (orange)

Isolating individual strokes and going into Blender spreadsheet editor was instrumental to getting a perfect match with Quill for each stroke type.

cube-brush Cubic brush stroke

In the end importing to mesh is now the best way to retain the most fidelity. On the other hand importing to Grease Pencil is best used when the Quill artwork only uses the cylindrical brush and for round trip scenarios where we know in advance that we are going to go back and forth.

Material

To handle color and opacity the add-on creates a simple material, shared by all objects in the imported scene, that reads the vertex color and opacity.

material

Quill can already export to Alembic and FBX so this not revolutionizing anything but it helps skip a step in some cases.

⚠️ The obvious limitation of importing the Quill paint strokes as mesh is that we can’t go back, it’s a one-way street. Maybe in the future the add-on could add extra attributes to the mesh so that the exporter may somehow rebuild the original strokes model out of it.

At this point importing animation is not supported, this will be for a future version.

Grease Pencil

The add-on can also import Quill drawings into Blender Grease Pencil.

Differences in data models

Grease Pencil data model is a “good fit” in the sense that at its core it is also a list of points in 3D space, each with an independent radius, color and opacity. Unfortunately the similarities stop there.

venn-diagram

⚠️ Quill has true 3D brushes: Ribbon, Cube, Ellipse… These don’t look the same depending on how the brush is held (cross section is not radially symmetric). In a sense the Quill stroke is the “swept volume” of the 3D shape of the brush. Grease Pencil brushes on the other hand don’t have a direction. They are like 2D self-orienting billboards. In practice this means the only way to have something that really looks the same in both is to use the cylindrical brush in Quill and the round brush in Blender.

ribbon-comparison Quill ribbon stroke (left) can't be correctly represented in Grease Pencil (right)

⚠️ Stroke endings or “caps” are treated differently. In Quill they just become part of the stroke data, as an additional point with zero radius. In Blender they are added on the fly by the renderer. This has consequence when we try to round trip the data. A corollary of this is that Grease Pencil allows strokes with a single point in them, they will look like discs. This is not supported in Quill, a stroke needs at least 3 points.

minimal Minimal stroke in Quill (left) and Grease Pencil (right)

❌ Quill has a directional opacity feature that is non existent in Grease Pencil, since the points don’t have any preferred “direction”.

❌ Grease Pencil strokes can be rendered with textured materials. This is used to create paint brush effects by some artists.

❌ Grease Pencil has a concept of “Fills” where a closed curve can be filled with a solid color or a texture. This is a fantastic feature for making large solid areas, sadly missing from Quill. In Quill the only thing similar is to use the ribbon brush but it can’t easily be molded into round shapes.

fill Grease Pencil Fill

❌ Grease Pencil can render the strokes in “stamp” mode where instead of the swept volume the stroke is made of discrete shapes located at the points. Not sure how useful this really is.

dots Quill stroke (left) rendered with Grease pencil "dots" stroke (right)

❌ Grease Pencil strokes can be set to “hold out”. Anything behind the stroke becomes invisible. In a 2D painting program this would be like using the background color to refine the shape of a foreground object, very useful in 2D, less so in 3D in my opinion, and inexistant in Quill.

❌ Grease Pencil objects actually contain a stack of layers and each layer has a blending mode (Hard Light, Add, Subtract, Multiply, Divide) with the layers below it. Similarly this seems mainly useful for flat scenes. In 3D the object “in front” is not the same depending on the perspective we are looking the scene from.

Despite these caveats this import option can still be useful, we just have to keep the limitations in mind:

  • For importing Quill into Grease Pencil, stick to using the cylindrical brush.
  • For exporting from Grease Pencil to Quill, stick to using solid strokes, no fills, no blending modes.

Differences in features

Differences in features and user experience work in our favor, it means we can use the features of one tool with data from the other.

Grease Pencil has superior support for modifying the strokes after the fact. We can grab specific points, add procedural wiggling, turn a static drawing into a progressive build-up, mix the stroke color with a global tint at the layer level, and many other features.

You have to dig around though, it’s a bit all over the place. Some stuff like changing width or opacity is accessible in sculpt mode. Other stuff is found in edit mode or in draw mode. Some settings are in the data tab, others in the material tab. And there are dedicated modifiers. It all makes sense in the end, but you have to be aware that there are many places a particular option could be in.

build Build modifier applied to Quill strokes

Exporting Blender to Quill

Exporting to Quill only really makes sense for a few of Blender’s object types. Mainly Grease Pencil, cameras and some specific assets for round-tripping like armatures. For other data types we can attempt a conversion that may make some sense, or the user can convert the assets to Grease Pencil first and export that.

Grease Pencil

Converting Grease Pencil to Quill paint strokes is possibly the most interesting part of the add-on to me. It only really works for a subset of features (see above) but it’s opening very interesting perspectives. The goal is basically to support everything that can be expected so I’m not going to go into details here. I maintain a list of supported features in the repository.

💡 Tip: to apply Grease Pencil modifiers that are static you can do it normally from the modifier menu. To bake animated modifiers like Build or Noise, go to Object > Animation > “Bake Object Transform to Grease Pencil…”.

For other object types special treatments are detailed below.

Mesh ➡️ wireframe

For mesh objects the add-on runs through the polygons and generates a straight stroke for each edge of each polygon. This way the mesh can be used as a reference or an envelope.

An alternative way to do this is to use Blender’s built-in converter to Grease Pencil. This way you can tweak the strokes width and color before the actual export. (Select the mesh then Object > Convert > Grease Pencil)

Yet another way to do it is to use another add-on: Grease Pencil From Mesh. This one has sharp edge filtering which will avoid creating strokes for internal edges on flat faces. The converter can also automatically set up the noise modifiers and it has other interesting features to better control which edges are generated.

wireframe Suzanne model, subdivided, converted to Grease Pencil, randomized, and exported to Quill

Armature ➡️ stick figure

For armature the add-on runs through the bones in rest pose and generates a straight stroke for each bone, with a random color. This can be used to paint over an existing armature that might be animated in a different program.

Note that after importing back into Blender you’ll still have to somehow assign the paint strokes to the armature, either via weight painting or by parenting strokes to the bones.

armature-quadruped Rigify meta-rig "Basic Quadruped" exported to Quill

Other object types

Similar to meshes, Blender itself can convert certain object types to Grease Pencil and then we can export that to Quill. This opens some interesting possibilities. This works quite well for anything based on curves. In object mode, menu Object > Convert > Grease Pencil.

For example this can be used to send text objects to Quill. The converter creates the outline of the text. Fills are ignored.

text Text outline with light noise exported to Quill

Future

Animation

Animation is a more complex topic than I anticipated when starting this project. Especially importing Quill model into Blender which I thought would be relatively straight forward, only because I had not really used Quill to its full capacity in this area.

Importing the “frame by frame” animation of Quill is simple enough, and that’s what the Alembic exporter in Quill does. But real-world animated scenes use looping, in/out visibility points that are independent from loops, offsets, groups with their own in/out visibility points that don’t impact the contained layers, and sequences with their own in/out and looping points which do impact the contained layers, plus groups and sequences that can be arbitrarily nested. This makes the model much more complex to process.

In the other direction Blender animation is probably even more complex just from the sheer number of ways we can animate things. Animating transforms with key frames, functions or drivers, parenting to animated objects, parenting to armature bones (rigidly), deformation from armature (weight painting), lattice deformation, shape keys, etc.

At least the frame by frame drawing system of Grease pencil should be compatible with frame by frame drawings in Quill.

Round trip

I want to investigate workflows that leverage both import and export, possibly back and forth multiple times.

One example was creating an armature in Blender, painting over it in Quill, and animating it back in Blender with inverse kinematics. Another idea is to import a Quill drawing, augment it with Grease Pencil and send it back to Quill.

Scene optimization

On the audience side VR scenes must keep their resources in check to run on the target hardware like the Quest. If the Grease Pencil or Mesh object generated by the importer could retain enough information to help the exporter recreate the original Quill strokes, we could imagine performing the optimization step programmatically in Blender python. This could open an avenue for interesting tricks.

More workflows

Let me know if you have workflows that could be helped by adding one feature or another. Lip sync? Cinematic camera moves? There is a lot to explore.


Back to homepage


comments powered by Disqus