Freestyle integration into Blender

December 21, 2009

New to-do list

Filed under: Notes — The dev team @ 12:58 AM

According to the recent change in the organization of the dev team, we think we need a new plan on how to proceed toward the merge into the trunk (Blender 2.5).  As a matter of fact, the merge itself has been agreed with Ton, who has contacted us once in a while with regard to the status of the branch.  The merge will take place when the dev team considers the branch is ready for production use.  The question is when the branch is considered production-ready.  It is hard to predict exactly when, but in my humble opinion, the branch will be ready for production use when most known instability issues are addressed and missing features are recovered.  Here is a list of known issues that should be fixed before the merge into the trunk:

  • Bug fix regarding the view map creation
    Most instability of the branch comes from bugs in the view map creation that detects feature edges (e.g., borders, crease edges, ridges/valleys, and suggestive contours) from a given scene.  A serious bug here is that objects behind the active camera cause a crash.  We have been working on a fix of the bug.  This seems closely related to a system design question of whether or not feature edges are clipped by the viewing frustum.
  • Bug fix regarding the Blender file loader
    As far as we known, the current Blender file loader (to feed meshes to the view map creation) does not correctly deal with several types of meshes (e.g., cloth and softbody).  The dev team has also been working on this issue.
  • Orthographic camera
    This is also part of the view map creation instability.

Here is another list of missing features to be reimplemented before the merge:

  • Textured strokes  
  • Transparent stroke colors
  • Z depth information
  • Stroke density information
  • Steerable view map

Both instability issues and missing features are listed in the descending priority order.  In addition, additional improvements and new features are anticipated (all with low priority, though).

  • Freestyle Python API improvements
    This has been almost done and existing style modules are expected to be rewritten according to a new, more Python-like iterator syntax.
  • Better feature edge detection
    If time permits, we consider improving the view map creation in two directions: 1) to detect feature edges at intersections of faces; and 2) to detect feature edges between two faces with different materials.

No specific deadline is specified for each item due to limited human resources.  Those Freestyle users who eagerly wait for the merge, thank you for your interest and for your understanding.


August 8, 2008

lib3ds removed

Filed under: Notes, Update — The dev team @ 9:23 AM

As of today, the integration is technically achieved: Freestyle runs natively in Blender without the use of any of its former libraries (Qt, SWIG, lib3ds). There are still remnants of libQGLViewer in the code, but they can considered part of Freestyle. This is great news as you should now be able to compile and test my branch on about every platform that Blender runs on. One thing to note is that only vlak-based shapes are supported. That means that strands or halos cannot be rendered by Freestyle yet (sorry, no BBB rendering this month…). I’ll see with Ton how to support these shape types.

Because I am going to SIGGRAPH tomorrow for a whole week and the Google Summer of Code ends on August 18, this is all that I would have been able to produce this summer. Looking back, I greatly underestimated the amount of work needed to replace SWIG. Removing SWIG and lib3ds took me about 5 weeks altogether, while I had only planned for 2-3 weeks. I also wasted two weeks trying to make renderbuffers work within Blender, which wouldn’t have been useful for the project anyway (strokes are to be rendered by Blender’s internal engine eventually).

I am proud of how far the project has gone but I am not totally satisfied. A lot more needs to be done:

  • a good amount of code needs to be refactored.
  • the last remaining style modules have to be supported.
  • Stéphane’s corrections have to be incorporated.
  • it would great to support a list of style modules, rather than just one.
  • the background paper texture should be removed eventually.
  • removing the need for an environment variable would be appreciated by non-technical users.

I’ll start making these changes today and I’ll continue again starting on August 18. Once I have strokes rendered by the Blender’s internal engine, I’ll continue implementing the original project proposal’s features, which will allow Freestyle to be manipulated and tweaked directly in the compositor via Python nodes.

Finally, due to requests, I have opened up comments on the blog. I won’t be able to monitor it much for a week so I hope it won’t get bogged down by spam…

July 31, 2008

Cross-language polymorphism support

Filed under: Notes, Update — The dev team @ 8:20 AM

I wanted to jump right on the lib3ds work before testing the new system further, but bad intuition kept me from doing that. I got intrigued by the errors I had gotten when testing and I was not content with the current work-around. Testing the system, I understood that the system was (read: is) unstable and unfinished. I realized it was missing an important feature, that Stéphane had mentioned very early in the project: cross-language polymorphism.

Basically, cross-language polymorphism allows the system to automagically support implementations in both language. If a C++ implementation is not present, the system tries to use the Python version if it is available. SWIG supports this feature through directors. To replace this feature, I had to study in more detail how directors are implemented in SWIG and how I could “recreate” them easily within our system.

The same way that I created a proxy mechanism to support Python to C++ redirection, I decided to support directors by having C++ objects store a reference to the Python object owning them. This gives  them the capability to “redirect” some of the execution back unto Python methods. If a C++ object uses the interface class method at runtime (instead of its own implementation), it will use the related Python object’s method if it is available, else will print an error message.

So far, the contour style module works correctly but style modules with shading function in Python do not work yet. To ease testing, I will (finally) have the style module selectable from the UI.

July 11, 2008

Quick feedback on SWIG removal advancement

Filed under: Notes, Other — The dev team @ 6:32 AM

I am very happy with my progress today. I have a greater understanding of how SWIG and the Python interpreter in Blender works. I feel like I have overcome the knowledge gap necessary to not only implement Freestyle’s Python API, but do it right. For short, I plan on implementing the bare essentials via the Python/C API and transfer the majority of the code in Python (currently done automatically by SWIG). I’ll remove all (currently strongly-typed) iterators for a list-based iteration and I’ll do away with all class casting code. I would want to elaborate on these details but frankly, I am exhausted… I have to get some sleep. I’ll try to describe my approach over the week-end, to make sure that I am getting this right. The resources that I found particularily helpful were:

On a totally irrelevant note, this blog received its first spam comment today. That’s something to celebrate.

July 9, 2008

SWIG replacement setup

Filed under: Notes, Update — The dev team @ 9:38 AM

I started implementing the SWIG replacement today. So far, it’s pretty empty… Based on the information available on Freestyle’s original Sourceforge site, I first recreated the class hierarchy. I originally wanted to manually go over each class’ interface and recreate each one of them. I realized that I could potentially miss some functions here and here. Since SWIG has worked without a hitch, I decided to reuse the wrapper files it generated. Using some ugly regular expressions, I was able to extract almost all of the wrapper functions’ definitions which will serve as a canvas for the implementation. I can’t really estimate how long it is going to take me to finish the task. I hope to have something to test in two days.

If you have tips that could help me on the renderbuffer problem, please do contact me. Any help is appreciated.

June 15, 2008

Phase 1 finished, on to Phase 2

Filed under: Notes, Questions, Update — The dev team @ 11:21 PM

I finished the first phase tonight: Freestyle has been successfully integrated as an external renderer. The only important feature missing is the ability to select a style module. After discussing with Jean-Luc, we consider the phase 1 finished because all of Freestyle’s basic functionally has been integrated. I will implement a Freestyle panel for style module selection in either phase 2 or 3.

I spent quite some time tonight integrating the camera’s information into Freestyle’s rendering pipeline. I first based the integration on Yafray’s code but it turned out to be a waste of time. I had to look at how Blender generates and stores the view matrix and the camera information. I modified Freestyle’s internal camera’s position, view direction and up vector. When I first tested the camera, I would get inconsistent results: now matter what position I placed my camera at, if I selected a particular object, I would always see the same result. By testing with multi-object scenes, I understood that the camera was somehow resetted after it had been defined. Looking a little bit closer, I found out that the Controller::Load3DSFile function was responsible for resetting the camera, via its call to AppGLWidget::FitBBox(). Uncommenting that function resolved the problem. Here are a few sample results with the corrected camera:

First thing to mention, the result is not exactly the same as the one produced by Blender’s internal engine. When I look closer at where the image is cut off, I can see that Freestyle’s result covers a larger area than Blender’s. I suspect that the cameras’ field of view are different (I haven’t modified Freestyle’s camera’s fov yet). I will of course get that adjusted soon. Here is a comparison of the slight differences:

I also need to mention that Freestyle will crash if the scene is empty or if the view frustrum does not have any object in it. I am guessing the original Freestyle made the scene emptiness test in other functions, so I will also need to add that check soon. For the time being, do not use Freestyle on an empty scene. More precisely, only the currently selected objects are exported by Blender’s Python 3ds export script, so only these objects will be rendered. It is important to call the render function on at least one selected object else it might potentially crash. I have not tested thoroughly if selecting the camera and/or lamps has an impact on Freestyle’s stability.

As of now, Freestyle will only render the “2D scene”, that is the series of strokes resulting from view map information. I decided to remove “3D scene” rendering because it is not one of Freestyle’s objectives and it is a waste of computing resources. In the same spirit, I am suggesting removing all background-rendering related code (the so-called “paper textures”): while it made sense for Freestyle’s original program, it is already taken care of by Blender (with the Sky render layer, for example). Freestyle has to be integrated as a “transparent” render layer, only containing the strokes built from the view map.

I am starting work on Freestyle’s render layer tomorrow. I will spend most of this week reading Blender’s internals, to understand how render layers are implemented and how SWIG can be replaced with Blender’s Python API. Since I finished phase 1 a week later than originally planned, I updated the project schedule with new deliverable dates. Phase 2 should end in three weeks on June 6 (right in time for GSoC midterm evaluation…). Its objective is the implementation of Freestyle’s own render layer.

May 28, 2008

First render !

Filed under: Notes, Update — The dev team @ 9:19 PM

I managed to get a little bit further. I corrected the GLStrokeRenderer::preparePaper function to properly load the paper texture (it took me a while to understand that ImBuf has a 32 bit image structure). I also set up the viewer to fixed dimensions 640×640, finally allowing the strokes to be rendered.

When I render the scene, the object shows up very quickly (I recognize the teapot’s shape) but is automatically erased by a gray background. I tried to grab a screenshot or a screencast of it, but it’s unfortunately impossible to do so. Since I did not make any change to the way things are rendered, I am guessing that when the render frame window is instantiated, its OpenGL context is selected for rendering. I have to analyze a little bit closer what’s going on to keep the result on display.

By studying Yafray’s code in more detail, I understood why the result was not kept on display. I first needed to correctly initialize the freestyleRender function in the rendering pipeline (creating a RenderResult structure to hold the image result and passing the Render variable to Freestyle’s execution function). In Freestyle’s rendering function, after having drawn the strokes, I copy the rendered strokes from the back buffer into a float array and use that data to load up the RenderResult‘s image. The result is then drawn into the render window.

Even though this revision is a big improvement over the past steps, phase 1 is still not over with. I am currently experiencing a few notable issues:

  • the paper texture is apparently not displayed (even though it is loaded) and the brush stroke color is inverted. Is that a Freestyle feature ? I do not know yet.
  • the view map computation may crash for some models (simpleScene.3DS for example) and other style modules (not tested yet).
  • Freestyle’s initialization is called each time the RENDER button is pressed, which could cause some memory leakage (the paper texture is reloaded each time without being deallocated at the end of the render).

After removing the fixed file references, I will need to change the UI to display a list of selectable style modules. Then, I will need to export the model data in 3DS format and the camera information to the viewer to finish up the first phase.

Here is a screenshot of what the first render looks like (the teapot is also upside-down in the original 3DS file):

May 1, 2008

Freestyle: canvas/stroke rendering reviewed

Filed under: Notes, Update — The dev team @ 7:37 PM

Today, I studied how the Canvas::Draw() method is implemented (see stroke/ folder). I wasn’t expected to dive so deep in the code. I was introduced to Freestyle’s stroke representation organization. This allows the user to render the result in different “formats”: OpenGL for live interaction (which can be saved to bitmap by the libQGLViewer widget), Postscript saving and text-format saving.

I now grasp the whole chain of command of rendering, from the canvas, to the layers, to the strokes. The first time a stroke is to be rendered, a stroke representation is created, which in turn generates the stroke’s underlying strip (the transformation of the stroke’s parameters into a OpenGL-friendly format). To get a better idea of how Freestyle’s abstract stroke model is transformed into triangle strips, I also studied the strip creation and strip cleaning code in detail. The stroke representation is then used by the stroke renderer to produce the end graphical result.

In Freestyle’s interface, the main canvas uses GLStrokeRenderer (found in rendering/) for the triangle strips rendering. Two rendering functions can be used (RenderStrokeRep and RenderStrokeRepBasic). So far, the only difference that I have found between them is that RenderStrokeRep allows for different stroke types (essentially different OpenGL blending models). The actual rendering is pretty straightforward: it sets up the appropriate OpenGL settings and then renders the stroke as a GL_TRIANGLE_STRIP, using the previously calculated color, alpha and texture.

Now that I understand how rendering is set up (cf. Controller.cpp) and implemented, I see where work needs to start. The most critical aspect of the first phase will be to make Freestyle independent of the libQGLViewer widget. The easy part is that I won’t need to replace libQGLViewer’s mouse/keyboard/view interaction capabilities. The hard part will be to recreate a new view that doesn’t break Freestyle’s internals and which can be saved to disk (Freestyle uses libQGLViewer’s saveSnapshot method for image saving).

I intend to proceed by first rendering a simple fixed scene (fixed camera, fixed object) using Freestyle’s code directly through the Blender UI. I’ll then need to insert Blender’s information (camera parameters and model data) into that pipeline, using Blender’s 3ds export capability. I can also sense that I will have quite a few problems to set up Freestyle’s compilation within Blender (dependency problems).

Blog at