Passing the torch

Other — maxime / December 8, 2009

After a week of consideration, I have come to the conclusion that it is best for me and the branch to discontinue my involvement in the Freestyle integration work. I am currently trying to focus on my software engineering career and unfortunately cannot devote enough time or energy to the branch.

Tamito has told me that he will continue to bring the branch forward. We have migrated the development blog to http://freestyleintegration.wordpress.com. You can update your RSS reader to point there, to stay in sync with the latest developments.

I do feel now is the right time to pass the torch to Tamito. Even though the overly ambitious plan was not realized entirely, I do feel that our work is in a state good enough to be brought to new heights by him and fresh blood. Sure, a lot of work has to be done to make it more robust and production-ready, but at least Freestyle can run independently without any external bindings within Blender. If anyone is interested in being involved in the branch, please contact Tamito or me. We will be glad to get you started.

I want to thank Ton Roosendaal, Jean-Luc Peurière and Stéphane Grabli, for giving me the opportunity to work on this fantastic project. I also cannot forget all of the beta testers from the BlenderArtists thread who have shared useful bug reports and constructive suggestions, the GraphicAll builders who have made the branch available to the whole world and the Blender development team who have patiently helped me on the IRC. The Blender community, both on the developers’ and the artists’ sides, is made up of many inspiring people. I have learned a lot from this project and I have nothing but thanks for your help and your support.


Carpe diem,

Maxime

Weekly update October 26-November 30

Update — T.K. / November 30, 2009

Since the last blog update, the dev team worked on internal improvements concerning the interface between Blender 2.5 and Freestyle. The context issue reported in the last blog post was partially addressed, and now the branch is capable of .blend files having multiple scenes. Freestyle module parameters can be independently specified for each scene. The compositor should work with multiple Freestyle-based scenes as expected.

The remaining context issue is the question of how to provide style modules with a way of accessing the current scene being rendered. We have been contacting core Blender developers to discuss the most appropriate extension of the Freestyle Python API design.

In the meantime, the branch has been merging those changes made in the trunk. On November 24 the long-awaited Blender 2.5 Alpha 0 was finally released. The Freestyle branch has incorporated all functionality in the Alpha 0 release including the new splash screen (below).

splash_tn

The render of the week is a set of images offerred by Nakeyta Schulz. Precise models, approximate ambient occlusion (AAO) and strokes with Freestyle all together result in a very descriptive appearance. Node-based hatching and “fake” tone are also nice additions. Check out his blog for more renders and the node setup for the fake tone. Thank you Nakeyta for sharing the Freestyle results with us!

nakeyta_1_tn nakeyta_2_tn
nakeyta_3_tn nakeyta_4_tn
nakeyta_5_tn nakeyta_6_tn
nakeyta_7_tn nakeyta_8_tn

Weekly update October 13-25

Update — T.K. / October 26, 2009

In the last 2 weeks, we fixed a few showstopper bugs found during the last minute of full Freestyle integration into Blender 2.5. Now the Freestyle branch should compile and run without a hitch.

In the meantime, the dev team was working on SVGWriter as a pilot investigation of the new RNA API in Blender 2.5. This made us aware of an API design issue in the branch concerning the notion of context. In Blender 2.5, a context refers to a target object to which a certain operation is applied. For example, rendering is an operation, and a scene and associated objects/data used in the scene comprise the context of the rendering operation. The problem is that as of this writing, Freestyle does not have a mechanism that allows style modules to access the context.

Contexts do not matter if you have only one scene in a .blend file, since all data is basically associated with the scene, and it is obvious which scene is the target of the rendering operation. When we have multiple scenes, however, we need a way to know which scene is the target. Try to open a new Blender window (Ctrl Alt w) and add a new scene (by pressing the “+” button next to the current scene name on top of a Blender window). Now you have 2 scenes being manipulated in 2 separate windows at the same time. You can start rendering with respect to each scene by hitting the render button in the corresponding window. Clearly, it does matter to know which scene should be rendered.

Note that the context issue is new in Blender 2.5. Before that, Blender was able to manipulate only one scene, since Blender did not have multiple main windows. That is why there was the notion of the current (active) scene, which can be retrieved by Blender.Scene.GetCurrent() in Python. In 2.5, this API function is no longer available.

As a temporary workaround, SVGWriter always refers to bpy.data.scenes[0] to get the first scene in the list of available scenes in a .blend file. This means that even if you have multiple scenes, the rest of the scenes never get rendered.

It is likely that the Freestyle Python API needs to be revised in the manner that style definitions are placed either in a method or in a function (instead of the module body) and a context is passed as an argument of the method/function. We are going to work on this in the next weeks.

Weekly update October 5-12

Update — maxime / October 14, 2009

Last week, Tamito completed the final integration of Freestyle into Blender 2.5 ! You should be able to recreate the images that you produced before the transition to the new codebase.

Below, you can find screenshots of the new interface. The workflow has not changed much:

  1. click the global Freestyle checkbox in the “Post Processing” section to enable/disable the use of Freestyle in the rendering pipeline. (first screenshot)
  2. click the layer Freestyle checkbox in the “Layers” section to enable/disable the use of Freestyle in the currently selected  layer. (second screenshot)
  3. as you toggle the checkbox, the layer’s style module configuration appears. You can set all of the parameters used by the pipeline: add (filechooser) or remove (black cross) style modules, determine the intended style module order (top/down arrows) or select the style modules to include in the render. (third screenshot)

You should be able to compile the latest branch revision without any problem. Tamito has reported that the program renders correctly on his Windows machine. I am experiencing crashes on my Macbook, due to OpenGL.

You might ask yourself why OpenGL is interfering with Freestyle: great question ! It is unfortunately one of the latest remaining dependency of Freestyle. Even though it is not used in any way during rendering, Freestyle’s internal texture manager still depends on it to instantiate the default stroke texture. Prior to the transition to the new codebase, the OpenGL code would not have any side-effect but things have apparently changed, at least on my machine.

This is clearly a showstopper. My short-term priority is to refactor Freestyle’s texture manager to use Blender’s texture system and have an almost fully-functional Freestyle soon. Let me know if you are experiencing similar crashes. We have also been merging the trunk code into our branch weekly so compiled binaries of our branch follow the latest 2.5 developments.

Enjoy !

Weekly update September 28 – October 4

Other — maxime / October 5, 2009

Integrating Freestyle in the RNA is going to be more difficult than we expected. For that reason, we have asked Brecht to help us in the upcoming week to make it happen. Once this is done, we will be able to reintegrate Freestyle in the UI. Unfortunately, we cannot work on other features until we can configure and run Freestyle in Blender 2.5. Thank you for your understanding.

Weekly update September 1-27

Update — maxime / September 28, 2009

In the first part of September, we tried to merge Freestyle’s branch into the 2.5 codebase, but faced a couple of issues with line endings that slowed us down. After getting some help from the core development team, we were able to continue with the current migration task.

Last week, we updated the codebase to work with Python3k. This week, we corrected all pending compilation problems related to 2.5 new architecture (especially the introduction of context-related functions). Freestyle is still not usable, even though the core of the work is done.

Next week, we will deal with the last part of the migration process, reintegrating Freestyle in the UI and adding it the necessary RNA code. Hopefully, by next week, you will all be able to play with Freestyle with the latest code in trunk !

Weekly update August 24-30

Update — T.K. / September 1, 2009

This week we addressed two issues in the Freestyle branch. One issue was concerned with the so-called background mode that was enabled by the “-b” command-line option, which led to an immediate crash of the program. Now that the issue has been addressed, you can render Freestyle-based still images and animation through Blender’s command-line interface. The other issue was related to undo. You may have observed that Blender abnormally terminates once in a while when you have canceled some editing operations by undoing. An attempt to fix this issue was made and now the branch should be more stable than ever.

As you can see with our discussion on the bf-committers mailing list, we are preparing for the merge. We first have to bring the 2.5 branch codebase into our branch, before we can merge them the changes into 2.5 trunk. Brecht will then help get RNA configured for Freestyle.

The render of the week is an animation movie by Mark Cannon (pyrosever). The render is based on a thoroughly rigged model of a tank. Thin brush strokes by Freestyle were put on top of well-textured solid faces, resulting in a sophisticated look. The movie clip on YouTube shows the tank in action — don’t miss the awesome movements of the continuous tracks (catapillars). Thank you pyrosever for sharing the nice results with all of us!

pyrosever_tn

Weekly update August 17-23

Update — maxime / August 24, 2009

Out of all the suggestions put forward for Freestyle’s new name in Blender, robi’s proposal is my favorite and, in my humble opinion, the most compact and best representative: ‘Styline’, short for stylized line. It is more concise and precise than ‘NPR rendering’ (too vague, encompasses too many artistic styles) or ’strokes’ (doesn’t really embody the process of shading/stylization and the fact that Freestyle operates on geometric line features). Styline is a descriptive noun that simplifies talking about layer composition (i.e. ’stylines are rendered over hair strands’), impossible with the current Freestyle name.

We are still awaiting approval from Blender core developers to merge Freestyle into the 2.5 branch. The team is already busy integrating this year’s GSoC projects so we have to wait for a couple more days or weeks. In the meantime, we have already determined the areas that need to be modified for our branch to compile with the 2.5 codebase (especially making it Python 3.1 compatible).

As soon as we go ahead with the merge, we will let you know on the blog. Thank you for your understanding.

The renders of the week were offered by devroo, who has successfully imitated hand-written pencil and brush strokes in the output (left).  A screenshot of Blender (right) shows how multiple render layers are combined using the compositor.  Thanks devroo for sharing the cool results!

devroo_output_tn devroo_setting_tn

Upcoming 2.5 branch integration

Update — maxime / August 17, 2009

We are in the process of merging the code base with the 2.5 branch. We have prepared two documents on the wiki, outlining the current status and an overview of the engine internals, and a FAQ on the blog. The FAQ and status report are meant for all users. Please take the time to read it, it might answer some of your current questions.

We hope to start merging this week. We are still waiting for feedback from the core Blender developers. It is important to note that development will halt on the Blender 2.48 branch. This might cause some interference with your work in the next few weeks. We do think moving on to the 2.5 branch now is better in the long term for all of us. We are sorry for the upcoming problems you might experience.

As Freestyle will be integrated in the main branch, we think it is an opportunity to rename it. “line strokes”, “line rendering” or “line drawing” are possible names. Let us know if you think of a better name !

The thread has also been quite busy recently (samples from devroo #515 #523 and pyroserver #530 #556, combination with ‘Painting with Polygons’ technique…).  We have seen the showcase of fantastic artwork by blendman, a French artist involved in the Moonkiroe MMORPG. As you can in the thread (#543, #550, #552, #561), blendman has utilized the Freestyle engine to recreate bande dessinée, a comics style used in France and Belgium. The results are simply breathtaking. Thank you blendman for sharing your work and your expertise with the rest of the community !

Recent updates of the Freestyle Python API

Update — T.K. / August 4, 2009

This blog entry summarizes the recent updates of the Freestyle Python API.  My commits in the past were mainly concerned with the so-called SWIG/directors dependency removal, aiming at an improved Python API.

1. Background

In Freestyle, the Python programming language is used to write a style that specifies how feature edges, extracted from a 3D scene, are transformed into strokes and drawn onto the canvas.  A style makes use of 5 operators: select (to choose edges of interest), chain (to transform edges into strokes), split (to separate strokes into smaller pieces), sort (to change the drawing order of strokes), and create (to draw strokes).  These 5 operators take as arguments a variety of rules for controlling the operations.  There are 4 types of rules: predicates (to test if a condition is satisfied), functions (to do some computations and return the results), chaining iterators (to specify how to concatenate edges to generate strokes), and stroke shaders (to modify strokes and their attributes such as colors and thickness).  These rules are defined by inheriting the following 8 base classes.

  • UnaryPredicate0D
  • UnaryPredicate1D
  • BinaryPredicate0D
  • BinaryPredicate1D
  • UnaryFunction0D
  • UnaryFunction1D
  • ChainingIterator
  • StrokeShader

Freestyle has many predefined predicates, functions, chaining iterators, and stroke shaders.  In addition, Freestyle allows users to define new rules by subclassing a base class and overriding a certain method.

Definition of a new rule usually requires access to Freestyle’s internal data structures such as feature edges (comprising a view map) and strokes.  Classes for the internal data structures, as well as the aforementioned classes for defining rules, are written in C++.  Moreover, basically every C++ class has the corresponding Python wrapper class that gives access to the C++ class.  There are roughly 150 Python wrappers for the same number of underlying C++ classes, and these Python wrappers as a whole constitute the Freestyle Python API.

Thanks to this multi-language API architecture, defining new rules by inheritance can be done not only in C++ but also in Python.  For example, UnaryPredicate0D is a base class in C++ for defining a predicate, and there is a corresponding Python class (or, strictly speaking, a C extension type) having the same name.  By subclassing the Python class and overriding a certain method (the __call__ method in this case), a new predicate can be easily defined in a style written in Python.

2. Issues that need addressing

Before being integrated into Blender, the original Freestyle had a Python API implemented using another program called SWIG.  This program automatically generates Python wrappers for a given set of C++ classes, reducing the cost of providing and maintaining Python wrappers.  On one hand, SWIG was a very useful tool for rapid development of Python wrappers.  On the other hand, however, there were some drawbacks in the auto-generated Python API from the viewpoint of its users (i.e., Python programmers), as well as from the viewpoint of developers, as detailed below.

2.1. User-visible issues

2.1.1. Iterators

The C++ classes in Freestyle heavily relied on iterators, and SWIG translated the iterators into Python while keeping the C++-like syntax.  For example, changing line thickness of a stroke can be written in Python in the SWIG-based iterator syntax as follows:

    it = stroke.StrokeVerticesBegin()
    while it.isEnd() == 0:
        v = it.getObject()
        v.attribute().setThickness(…)
        it.increment()

Python has its own language features for iteration, so appropriate support for more Python-like iterator syntax was desired.

2.1.2. Cast operators

Freestyle’s C++ classes also relied on cast operators to convert a class instance into a subclass and vice versa.  For example, ViewVertex (representing a vertex in a view map) has two subclasses TVertex (a vertex at an intersection of two edges) and NonTVertex.  A feature edge is represented by a series ViewVertex instances, each of which is actually either a TVertex or NonTVertex instance.  If a subclass-specific method is needed to do some useful thing on the edge, then a cast operator is used to convert a ViewVertex object into TVertex/NonTVertex.  Python does not have the notion of cast operations, so that the ViewVertex wrapper in the SWIG-based Python API had two auxiliary methods castToTVertex() and castToNonTVertex().  There was a number of such castToSomething methods in the SWIG-based Python API, and they looked too awkward.  Since Python is a dynamically typed programming language, a better approach would be possible to make the cast operators unnecessary.

2.1.3. Better support for Python data types

Besides the cast operators, better support for Python’s built-in data types was preferable.  For example, being able to pass lists and tuples would be useful when vectors are required as method arguments.  In addition, Python has a variety of boolean expressions that are equivalent to True and False.  Taking these Python-specific language features into account would contribute to the flexibility of the Python API.

2.2. Implementation issues

2.2.1. The “directors”

The SWIG-based implementation of the Python API relied heavily on a set of functions called directors.  A director is a piece of code for getting a Python class method called from within a C++ class method.  Directors were necessary to enable the cross-language inheritance to define new rules in Python, and they made the API implementation quite difficult to understand and maintain.

2.2.2. Borrowed references

Some classes in the Python API are mainly used to wrap underlying C++ objects that comprise Freestyle’s internal data structures.  If that is the case, the wrapping Python classes  instantiated by Freestyle, and they retain a “borrowed” reference to a wrapped C++ object that is part of the internal data structures.  In addition, these Python classes can also be instantiated by a user.  In the latter case, wrapped C++ objects are typically just a copy of an internal object.  Strictly managing borrowed references to wrapped internal objects was an important issue, since releasing internal objects by mistake easily leads to a crash of Blender, while not releasing copied objects results in memory leaks.

3. Solutions implemented

We took the present opportunity of the Freestyle integrate into Blender to address the above-mentioned issues and make the Freestyle Python API more user-friendly.  The recent commits on the Python API were all intended to address these SWIG-related issues.  Following sections summarize the implemented solutions for each issue.

3.1. Iterators

In addition to the SWIG-based iterator syntax, Python’s native iterator protocol has been implemented.  The Python wrapper classes that support the iterator protocol are: AdjacencyIterator, Interface0DIterator, orientedViewEdgeIterator, Stroke and StrokeVertexIterator.  For example, the previous SWIG-based iterator syntax example can be rewritten as follows:

    for v in stroke:
        v.attribute().setThickness(…)

Related commits:
17661 (Stroke, StrokeIterator)
21889, 22003 (AdjacencyIterator)
22096 (Interface0DIterator, orientedViewEdgeIterator)
22099 (increment/decrement boundary checking)

3.2. Cast operators

Instead of relying on explicit cast operators through the castToSomething methods, introspection-based automatic type conversion has been implemented.  When a C++ class method returns an instance of a generic class (e.g., ViewVertex), the corresponding Python class method returns an instance of a specific class (e.g., TVertex or NonTVertex).  This made all castToSomething methods no longer necessary, so that they were removed from the Python API.  If you need to rewrite old style modules that rely on the cast methods, apply the following rewriting rules:

a) The following 5 methods return an object itself, so just removing a method call will suffice.

  • SVertex.castToSVertex()
  • TVertex.castToViewVertex()
  • TVertex.castToTVertex()
  • NonTVertex.castToViewVertex()
  • NonTVertex.castToNonTVertex()

If you need to handle objects in a different way depending on their types, then you can use Python’s type checking idioms such as “type(obj) is T” and “isinstance(obj, T)”.  Example:

    [Original]
    v = it.getObject()
    # try to convert v into a TVertex object
    vertex = v.castToTVertex()
    if vertex != None:
        … # do something on the TVertex object
    # try to convert v into a NonTVertex object
    vertex = v.castToNonTVertex()
    if vertex != None:
        … # do something on the NonTVertex object

    [Rewritten]
    vertex = it.getObject()
    if type(vertex) is TVertex:
        … # do something on the TVertex object
    elif type(vertex) is NonTVertex:
        … # do something on the NonTVertex object

b) Use SVertex.viewvertex() instead of the following 3 methods.  You don’t need to care about which cast method is appropriate.  SVertex.viewvertex() does, if necessary, introspection-based automatic type conversion for you.

  • SVertex.castToViewVertex()
  • SVertex.castToTVertex()
  • SVertex.castToNonTVertex()

c) Use NonTVertex.svertex() instead of the following method.

  • NonTVertex.castToSVertex()

d) Replace the following method with the Python equivalent shown below.

  • CurvePoint.castToSVertex()

Let cp be a CurvePoint object, then this method can be expressed as follows:

    if cp.t2d() == 0.0:
        return cp.A() # returns an SVertex
    elif cp.t2d() == 1.0:
        return cp.B() # returns an SVertex
    return None

e) Similarly, the following 3 methods

  • CurvePoint.castToViewVertex()
  • CurvePoint.castToTVertex()
  • CurvePoint.castToNonVertex()

can be expressed as follows:

    if cp.t2d() == 0.0:
        return cp.A().viewvertex()
    elif cp.t2d() == 1.0:
        return cp.B().viewvertex()
    return None

Related commits:
21710, 22148 (introspection-based automatic type conversion)
21947 (updates in freestyle_init.py, removal of castToTVertex method calls)
22153 (removal of all castToSomething methods from Interface0D subclasses)

3.3. Better support for Python data types

The Freestyle Python API now employs the Vector class in the Blender.Mathutils module to represent 2D and 3D vectors.  Python class methods accepting vectors also accept lists and tuples if they have the same number of elements.  Boolean expressions in Python are also better supported.

Related commits:
21877, 21947 (vectors, lists and tuples)
19526 (boolean)

3.4. The “directors”

The dependency on directors has been largely simplified.  Now the Freestyle Python API clearly defines that only the following class methods can be overridden by Python subclasses to write user-defined rules:

  • UnaryPredicate0D.__call__()
  • UnaryPredicate1D.__call__()
  • BinaryPredicate0D.__call__()
  • BinaryPredicate1D.__call__()
  • UnaryFunction0DDouble.__call__()
  • UnaryFunction0DEdgeNature.__call__()
  • UnaryFunction0DFloat.__call__()
  • UnaryFunction0DId.__call__()
  • UnaryFunction0DMaterial.__call__()
  • UnaryFunction0DUnsigned.__call__()
  • UnaryFunction0DVec2f.__call__()
  • UnaryFunction0DVec3f.__call__()
  • UnaryFunction0DVectorViewShape.__call__()
  • UnaryFunction0DViewShape.__call__()
  • UnaryFunction1DDouble.__call__()
  • UnaryFunction1DEdgeNature.__call__()
  • UnaryFunction1DFloat.__call__()
  • UnaryFunction1DUnsigned.__call__()
  • UnaryFunction1DVec2f.__call__()
  • UnaryFunction1DVec3f.__call__()
  • UnaryFunction1DVectorViewShape.__call__()
  • UnaryFunction1DVoid.__call__()
  • ChainingIterator.init()
  • ChainingIterator.traverse()
  • StrokeShader.shade()

For the purpose of reducing various complications due to the directors, it appeared vital to determine which classes (and more specifically which of their methods) can be inherited (overridden) by user-defined Python subclasses.  The above list of overridable Python class methods was determined by the following reasons.

  1. Classes in Freestyle are roughly classified into three categories: 1) those classes for building Freestyle’s internal data structures; 2) classes for predefined predicates, functions, chaining iterators, and stroke shaders; and 3) base classes for defining new rules.  Inheriting Python classes in the first category does not make sense, since their main purpose is simply to give access to the internal data structures.  Derived classes for predefined rules are also not intended to be further subclassed, since their __call__ methods are rather monolithic and not extensible.  Consequently, only the classes in the third category are considered suitable as base classes for user-defined subclasses.
  2. Since the Freestyle core is written in C++, a director is necessary whenever a user-defined rule in Python has to be called within from the C++ side.  In fact, the C++ class methods being wrapped by the Python class methods listed above employ a director to make this happen.  On the other hand, Python class methods may be invoked within from the Python side (for example, AndUP1D and OrUP1D, written in Python, invoke the __call__ methods of given predicate classes).  Therefore, an appropriate measure is necessary to avoid infinite recursion between the C++ and Python sides.  Having a director call in every C++ class method together with a measure for infinite loop prevention is costly in terms of code maintenance and very likely to be useless.  Therefore, keeping the list of director-dependent class methods as small as possible is desired.

Related commits:
22096, 22148 (simplification of directors)
21930, 22002, 22093 (infinite loop prevention)

3.5. Borrowed references

A “borrowed” flag was added to the definitions of relevant Python classes in order to indicate whether or not a Python wrapper object has a reference to a C++ object that comprises the internal data structures.  The deconstructors of the Python classes check this flag and release a wrapped C++ object only when it is not part of the internal data structures.  The following Python classes were modified: Interface1D and its subclasses (Chain, FEdge, FEdgeSharp, FEdgeSmooth, FrsCurve, Stroke, ViewEdge), Interface0D and its subclasses (CurvePoint, NonTVertex, SVertex, StrokeVertex, TVertex, ViewVertex), FrsMaterial, SShape, StrokeAttribute, and ViewShape.

Related commits:
22148 (borrowed references)

3.6. Additional improvements

The Freesytle Python API is fairly large and its implementation is now done manually, so that complete functionality coverage of all C++ classes is an important goal.  Some of the recent commits are related to this issue, and those functionalities in C++ that are required by standard styles have been already available.

Another issue is stability, since the process of writing a style in Python is inevitably error-prone.  Runtime errors should be properly trapped by the API implementation and reported to users in the form of Python exceptions.  To improve the stability of application programs (i.e., styles), error handling in both C++ and Python layers has been largely enhanced.  Now most trivial programming mistakes just result in a Python traceback and do not break Blender.

Related commits:
19342 (better error handling on the C++ side)
21921 (context functions)
21946, 21947 (undocumented utility functions)

4. Summary

The most significant update for general Freestyle users is that now all standard styles that come with Freestyle for Blender should run without any Python traceback, thanks to the above-mentioned Python API improvements and consolidation.  There are still missing features such as textured strokes and steerable view maps, and some styles may not yield any strokes.  The rest of the standard styles, however, are expected to work fine.

Our future work on the Python API includes more improvements for stability, better integration of Blender-specific data types, and API documentation updates.

Next Page »
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
(c) 2010 Freestyle integration into Blender | powered by WordPress with Barecity