0548d49dce002ba7566dd2e31bf979dd8196b3f0
[blender.git] / source / blender / freestyle / intern / blender_interface / FRS_freestyle.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
22  *  \ingroup freestyle
23  */
24
25 #include <iostream>
26 #include <map>
27 #include <set>
28
29 #include "../application/AppCanvas.h"
30 #include "../application/AppConfig.h"
31 #include "../application/AppView.h"
32 #include "../application/Controller.h"
33
34 #include "BlenderStrokeRenderer.h"
35
36 using namespace std;
37 using namespace Freestyle;
38
39 extern "C" {
40
41 #include "MEM_guardedalloc.h"
42
43 #include "DNA_camera_types.h"
44 #include "DNA_collection_types.h"
45 #include "DNA_freestyle_types.h"
46 #include "DNA_material_types.h"
47 #include "DNA_text_types.h"
48
49 #include "BKE_context.h"
50 #include "BKE_freestyle.h"
51 #include "BKE_global.h"
52 #include "BKE_library.h"
53 #include "BKE_linestyle.h"
54 #include "BKE_text.h"
55
56 #include "BLT_translation.h"
57
58 #include "BLI_blenlib.h"
59 #include "BLI_math.h"
60 #include "BLI_math_color_blend.h"
61 #include "BLI_callbacks.h"
62
63 #include "BPY_extern.h"
64
65 #include "renderpipeline.h"
66
67 #include "FRS_freestyle.h"
68
69 #define DEFAULT_SPHERE_RADIUS 1.0f
70 #define DEFAULT_DKR_EPSILON   0.0f
71
72 struct FreestyleGlobals g_freestyle;
73
74 // Freestyle configuration
75 static bool freestyle_is_initialized = false;
76 static Config::Path *pathconfig = NULL;
77 static Controller *controller = NULL;
78 static AppView *view = NULL;
79
80 // line set buffer for copy & paste
81 static FreestyleLineSet lineset_buffer;
82 static bool lineset_copied = false;
83
84
85 static void load_post_callback(struct Main * /*main*/, struct ID * /*id*/, void * /*arg*/)
86 {
87         lineset_copied = false;
88 }
89
90 static bCallbackFuncStore load_post_callback_funcstore = {
91         NULL, NULL, /* next, prev */
92         load_post_callback, /* func */
93         NULL, /* arg */
94         0 /* alloc */
95 };
96
97 //=======================================================
98 //   Initialization
99 //=======================================================
100
101 void FRS_initialize()
102 {
103         if (freestyle_is_initialized)
104                 return;
105
106         pathconfig = new Config::Path;
107         controller = new Controller();
108         view = new AppView;
109         controller->setView(view);
110         controller->Clear();
111         g_freestyle.scene = NULL;
112         lineset_copied = false;
113
114         BLI_callback_add(&load_post_callback_funcstore, BLI_CB_EVT_LOAD_POST);
115
116         freestyle_is_initialized = 1;
117 }
118
119 void FRS_set_context(bContext *C)
120 {
121         if (G.debug & G_DEBUG_FREESTYLE) {
122                 cout << "FRS_set_context: context 0x" << C << " scene 0x" << CTX_data_scene(C) << endl;
123         }
124         controller->setContext(C);
125 }
126
127 void FRS_exit()
128 {
129         delete pathconfig;
130         delete controller;
131         delete view;
132 }
133
134 //=======================================================
135 //   Rendering
136 //=======================================================
137
138 static void init_view(Render *re)
139 {
140         int width = re->winx;
141         int height = re->winy;
142         int xmin = re->disprect.xmin;
143         int ymin = re->disprect.ymin;
144         int xmax = re->disprect.xmax;
145         int ymax = re->disprect.ymax;
146
147         float thickness = 1.0f;
148         switch (re->r.line_thickness_mode) {
149                 case R_LINE_THICKNESS_ABSOLUTE:
150                         thickness = re->r.unit_line_thickness * (re->r.size / 100.f);
151                         break;
152                 case R_LINE_THICKNESS_RELATIVE:
153                         thickness = height / 480.f;
154                         break;
155         }
156
157         g_freestyle.viewport[0] = g_freestyle.viewport[1] = 0;
158         g_freestyle.viewport[2] = width;
159         g_freestyle.viewport[3] = height;
160
161         view->setWidth(width);
162         view->setHeight(height);
163         view->setBorder(xmin, ymin, xmax, ymax);
164         view->setThickness(thickness);
165
166         if (G.debug & G_DEBUG_FREESTYLE) {
167                 cout << "\n===  Dimensions of the 2D image coordinate system  ===" << endl;
168                 cout << "Width  : " << width << endl;
169                 cout << "Height : " << height << endl;
170                 if (re->r.mode & R_BORDER)
171                         cout << "Border : (" << xmin << ", " << ymin << ") - (" << xmax << ", " << ymax << ")" << endl;
172                 cout << "Unit line thickness : " << thickness << " pixel(s)" << endl;
173         }
174 }
175
176 static void init_camera(Render *re)
177 {
178         // It is assumed that imported meshes are in the camera coordinate system.
179         // Therefore, the view point (i.e., camera position) is at the origin, and
180         // the model-view matrix is simply the identity matrix.
181
182         zero_v3(g_freestyle.viewpoint);
183
184         unit_m4(g_freestyle.mv);
185
186         copy_m4_m4(g_freestyle.proj, re->winmat);
187
188 #if 0
189         print_m4("mv", g_freestyle.mv);
190         print_m4("proj", g_freestyle.proj);
191 #endif
192 }
193
194 static char *escape_quotes(char *name)
195 {
196         char *s = (char *)MEM_mallocN(strlen(name) * 2 + 1, "escape_quotes");
197         char *p = s;
198         while (*name) {
199                 if (*name == '\'')
200                         *(p++) = '\\';
201                 *(p++) = *(name++);
202         }
203         *p = '\0';
204         return s;
205 }
206
207 static char * create_lineset_handler(char *layer_name, char *lineset_name)
208 {
209         const char *fmt = "__import__('parameter_editor').process('%s', '%s')\n";
210         char *s1 = escape_quotes(layer_name);
211         char *s2 = escape_quotes(lineset_name);
212         char *text = BLI_sprintfN(fmt, s1, s2);
213         MEM_freeN(s1);
214         MEM_freeN(s2);
215         return text;
216 }
217
218 struct edge_type_condition
219 {
220         int edge_type, value;
221 };
222
223 // examines the conditions and returns true if the target edge type needs to be computed
224 static bool test_edge_type_conditions(struct edge_type_condition *conditions,
225                                       int num_edge_types, bool logical_and, int target, bool distinct)
226 {
227         int target_condition = 0;
228         int num_non_target_positive_conditions = 0;
229         int num_non_target_negative_conditions = 0;
230
231         for (int i = 0; i < num_edge_types; i++) {
232                 if (conditions[i].edge_type == target)
233                         target_condition = conditions[i].value;
234                 else if (conditions[i].value > 0)
235                         ++num_non_target_positive_conditions;
236                 else if (conditions[i].value < 0)
237                         ++num_non_target_negative_conditions;
238         }
239         if (distinct) {
240                 // In this case, the 'target' edge type is assumed to appear on distinct edge
241                 // of its own and never together with other edge types.
242                 if (logical_and) {
243                         if (num_non_target_positive_conditions > 0)
244                                 return false;
245                         if (target_condition > 0)
246                                 return true;
247                         if (target_condition < 0)
248                                 return false;
249                         if (num_non_target_negative_conditions > 0)
250                                 return true;
251                 }
252                 else {
253                         if (target_condition > 0)
254                                 return true;
255                         if (num_non_target_negative_conditions > 0)
256                                 return true;
257                         if (target_condition < 0)
258                                 return false;
259                         if (num_non_target_positive_conditions > 0)
260                                 return false;
261                 }
262         }
263         else {
264                 // In this case, the 'target' edge type may appear together with other edge types.
265                 if (target_condition > 0)
266                         return true;
267                 if (target_condition < 0)
268                         return true;
269                 if (logical_and) {
270                         if (num_non_target_positive_conditions > 0)
271                                 return false;
272                         if (num_non_target_negative_conditions > 0)
273                                 return true;
274                 }
275                 else {
276                         if (num_non_target_negative_conditions > 0)
277                                 return true;
278                         if (num_non_target_positive_conditions > 0)
279                                 return false;
280                 }
281         }
282         return true;
283 }
284
285 static void prepare(Render *re, ViewLayer *view_layer)
286 {
287         // load mesh
288         re->i.infostr = IFACE_("Freestyle: Mesh loading");
289         re->stats_draw(re->sdh, &re->i);
290         re->i.infostr = NULL;
291         if (controller->LoadMesh(re, view_layer)) // returns if scene cannot be loaded or if empty
292                 return;
293         if (re->test_break(re->tbh))
294                 return;
295
296         // add style modules
297         FreestyleConfig *config = &view_layer->freestyle_config;
298
299         if (G.debug & G_DEBUG_FREESTYLE) {
300                 cout << "\n===  Rendering options  ===" << endl;
301         }
302         int layer_count = 0;
303
304         switch (config->mode) {
305                 case FREESTYLE_CONTROL_SCRIPT_MODE:
306                         if (G.debug & G_DEBUG_FREESTYLE) {
307                                 cout << "Modules :" << endl;
308                         }
309                         for (FreestyleModuleConfig *module_conf = (FreestyleModuleConfig *)config->modules.first;
310                              module_conf;
311                              module_conf = module_conf->next)
312                         {
313                                 if (module_conf->script && module_conf->is_displayed) {
314                                         const char *id_name = module_conf->script->id.name + 2;
315                                         if (G.debug & G_DEBUG_FREESTYLE) {
316                                                 cout << "  " << layer_count + 1 << ": " << id_name;
317                                                 if (module_conf->script->name)
318                                                         cout << " (" << module_conf->script->name << ")";
319                                                 cout << endl;
320                                         }
321                                         controller->InsertStyleModule(layer_count, id_name, module_conf->script);
322                                         controller->toggleLayer(layer_count, true);
323                                         layer_count++;
324                                 }
325                         }
326                         if (G.debug & G_DEBUG_FREESTYLE) {
327                                 cout << endl;
328                         }
329                         controller->setComputeRidgesAndValleysFlag((config->flags & FREESTYLE_RIDGES_AND_VALLEYS_FLAG) ? true : false);
330                         controller->setComputeSuggestiveContoursFlag((config->flags & FREESTYLE_SUGGESTIVE_CONTOURS_FLAG) ? true : false);
331                         controller->setComputeMaterialBoundariesFlag((config->flags & FREESTYLE_MATERIAL_BOUNDARIES_FLAG) ? true : false);
332                         break;
333                 case FREESTYLE_CONTROL_EDITOR_MODE:
334                         int use_ridges_and_valleys = 0;
335                         int use_suggestive_contours = 0;
336                         int use_material_boundaries = 0;
337                         struct edge_type_condition conditions[] = {
338                                 {FREESTYLE_FE_SILHOUETTE, 0},
339                                 {FREESTYLE_FE_BORDER, 0},
340                                 {FREESTYLE_FE_CREASE, 0},
341                                 {FREESTYLE_FE_RIDGE_VALLEY, 0},
342                                 {FREESTYLE_FE_SUGGESTIVE_CONTOUR, 0},
343                                 {FREESTYLE_FE_MATERIAL_BOUNDARY, 0},
344                                 {FREESTYLE_FE_CONTOUR, 0},
345                                 {FREESTYLE_FE_EXTERNAL_CONTOUR, 0},
346                                 {FREESTYLE_FE_EDGE_MARK, 0}
347                         };
348                         int num_edge_types = sizeof(conditions) / sizeof(struct edge_type_condition);
349                         if (G.debug & G_DEBUG_FREESTYLE) {
350                                 cout << "Linesets:" << endl;
351                         }
352                         for (FreestyleLineSet *lineset = (FreestyleLineSet *)config->linesets.first;
353                              lineset;
354                              lineset = lineset->next)
355                         {
356                                 if (lineset->flags & FREESTYLE_LINESET_ENABLED) {
357                                         if (G.debug & G_DEBUG_FREESTYLE) {
358                                                 cout << "  " << layer_count+1 << ": " << lineset->name << " - " <<
359                                                         (lineset->linestyle ? (lineset->linestyle->id.name + 2) : "<NULL>") << endl;
360                                         }
361                                         char *buffer = create_lineset_handler(view_layer->name, lineset->name);
362                                         controller->InsertStyleModule(layer_count, lineset->name, buffer);
363                                         controller->toggleLayer(layer_count, true);
364                                         MEM_freeN(buffer);
365                                         if (!(lineset->selection & FREESTYLE_SEL_EDGE_TYPES) || !lineset->edge_types) {
366                                                 ++use_ridges_and_valleys;
367                                                 ++use_suggestive_contours;
368                                                 ++use_material_boundaries;
369                                         }
370                                         else {
371                                                 // conditions for feature edge selection by edge types
372                                                 for (int i = 0; i < num_edge_types; i++) {
373                                                         if (!(lineset->edge_types & conditions[i].edge_type))
374                                                                 conditions[i].value = 0; // no condition specified
375                                                         else if (!(lineset->exclude_edge_types & conditions[i].edge_type))
376                                                                 conditions[i].value = 1; // condition: X
377                                                         else
378                                                                 conditions[i].value = -1; // condition: NOT X
379                                                 }
380                                                 // logical operator for the selection conditions
381                                                 bool logical_and = ((lineset->flags & FREESTYLE_LINESET_FE_AND) != 0);
382                                                 // negation operator
383                                                 if (lineset->flags & FREESTYLE_LINESET_FE_NOT) {
384                                                         // convert an Exclusive condition into an Inclusive equivalent using De Morgan's laws:
385                                                         //   NOT (X OR Y) --> (NOT X) AND (NOT Y)
386                                                         //   NOT (X AND Y) --> (NOT X) OR (NOT Y)
387                                                         for (int i = 0; i < num_edge_types; i++)
388                                                                 conditions[i].value *= -1;
389                                                         logical_and = !logical_and;
390                                                 }
391                                                 if (test_edge_type_conditions(conditions, num_edge_types, logical_and,
392                                                                               FREESTYLE_FE_RIDGE_VALLEY, true))
393                                                 {
394                                                         ++use_ridges_and_valleys;
395                                                 }
396                                                 if (test_edge_type_conditions(conditions, num_edge_types, logical_and,
397                                                                               FREESTYLE_FE_SUGGESTIVE_CONTOUR, true))
398                                                 {
399                                                         ++use_suggestive_contours;
400                                                 }
401                                                 if (test_edge_type_conditions(conditions, num_edge_types, logical_and,
402                                                                               FREESTYLE_FE_MATERIAL_BOUNDARY, true))
403                                                 {
404                                                         ++use_material_boundaries;
405                                                 }
406                                         }
407                                         layer_count++;
408                                 }
409                         }
410                         controller->setComputeRidgesAndValleysFlag(use_ridges_and_valleys > 0);
411                         controller->setComputeSuggestiveContoursFlag(use_suggestive_contours > 0);
412                         controller->setComputeMaterialBoundariesFlag(use_material_boundaries > 0);
413                         break;
414         }
415
416         // set parameters
417         if (config->flags & FREESTYLE_ADVANCED_OPTIONS_FLAG) {
418                 controller->setSphereRadius(config->sphere_radius);
419                 controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon);
420         }
421         else {
422                 controller->setSphereRadius(DEFAULT_SPHERE_RADIUS);
423                 controller->setSuggestiveContourKrDerivativeEpsilon(DEFAULT_DKR_EPSILON);
424         }
425         controller->setFaceSmoothness((config->flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) ? true : false);
426         controller->setCreaseAngle(RAD2DEGF(config->crease_angle));
427         controller->setVisibilityAlgo((config->flags & FREESTYLE_CULLING) ?
428                                       FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE :
429                                       FREESTYLE_ALGO_ADAPTIVE_CUMULATIVE);
430
431         if (G.debug & G_DEBUG_FREESTYLE) {
432                 cout << "Crease angle : " << controller->getCreaseAngle() << endl;
433                 cout << "Sphere radius : " << controller->getSphereRadius() << endl;
434                 cout << "Face smoothness : " << (controller->getFaceSmoothness() ? "enabled" : "disabled") << endl;
435                 cout << "Ridges and valleys : " <<
436                         (controller->getComputeRidgesAndValleysFlag() ? "enabled" : "disabled") << endl;
437                 cout << "Suggestive contours : " <<
438                         (controller->getComputeSuggestiveContoursFlag() ? "enabled" : "disabled") << endl;
439                 cout << "Suggestive contour Kr derivative epsilon : " <<
440                         controller->getSuggestiveContourKrDerivativeEpsilon() << endl;
441                 cout << "Material boundaries : " <<
442                         (controller->getComputeMaterialBoundariesFlag() ? "enabled" : "disabled") << endl;
443                 cout << endl;
444         }
445
446         // set diffuse and z depth passes
447         RenderLayer *rl = RE_GetRenderLayer(re->result, view_layer->name);
448         bool diffuse = false, z = false;
449         for (RenderPass *rpass = (RenderPass *)rl->passes.first; rpass; rpass = rpass->next) {
450                 if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) {
451                         controller->setPassDiffuse(rpass->rect, rpass->rectx, rpass->recty);
452                         diffuse = true;
453                 }
454                 if (STREQ(rpass->name, RE_PASSNAME_Z)) {
455                         controller->setPassZ(rpass->rect, rpass->rectx, rpass->recty);
456                         z = true;
457                 }
458         }
459         if (G.debug & G_DEBUG_FREESTYLE) {
460                 cout << "Passes :" << endl;
461                 cout << "  Diffuse = " << (diffuse ? "enabled" : "disabled") << endl;
462                 cout << "  Z = " << (z ? "enabled" : "disabled") << endl;
463         }
464
465         if (controller->hitViewMapCache())
466                 return;
467
468         // compute view map
469         re->i.infostr = IFACE_("Freestyle: View map creation");
470         re->stats_draw(re->sdh, &re->i);
471         re->i.infostr = NULL;
472         controller->ComputeViewMap();
473 }
474
475 void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_render)
476 {
477         RenderLayer *rl;
478         float *src, *dest, *pixSrc, *pixDest;
479         int x, y, rectx, recty;
480
481         if (freestyle_render == NULL || freestyle_render->result == NULL)
482                 return;
483
484         rl = render_get_active_layer( freestyle_render, freestyle_render->result );
485         if (!rl) {
486                 if (G.debug & G_DEBUG_FREESTYLE) {
487                         cout << "No source render layer to composite" << endl;
488                 }
489                 return;
490         }
491
492         src = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, freestyle_render->viewname);
493         if (!src) {
494                 if (G.debug & G_DEBUG_FREESTYLE) {
495                         cout << "No source result image to composite" << endl;
496                 }
497                 return;
498         }
499 #if 0
500         if (G.debug & G_DEBUG_FREESTYLE) {
501                 cout << "src: " << rl->rectx << " x " << rl->recty << endl;
502         }
503 #endif
504
505         rl = RE_GetRenderLayer(re->result, view_layer->name);
506         if (!rl) {
507                 if (G.debug & G_DEBUG_FREESTYLE) {
508                         cout << "No destination render layer to composite to" << endl;
509                 }
510                 return;
511         }
512         dest = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, re->viewname);
513         if (!dest) {
514                 if (G.debug & G_DEBUG_FREESTYLE) {
515                         cout << "No destination result image to composite to" << endl;
516                 }
517                 return;
518         }
519 #if 0
520         if (G.debug & G_DEBUG_FREESTYLE) {
521                 cout << "dest: " << rl->rectx << " x " << rl->recty << endl;
522         }
523 #endif
524
525         rectx = re->rectx;
526         recty = re->recty;
527         for (y = 0; y < recty; y++) {
528                 for (x = 0; x < rectx; x++) {
529                         pixSrc = src + 4 * (rectx * y + x);
530                         if (pixSrc[3] > 0.0) {
531                                 pixDest = dest + 4 * (rectx * y + x);
532                                 blend_color_mix_float(pixDest, pixDest, pixSrc);
533                         }
534                 }
535         }
536 }
537
538 static int displayed_layer_count(ViewLayer *view_layer)
539 {
540         int count = 0;
541
542         switch (view_layer->freestyle_config.mode) {
543                 case FREESTYLE_CONTROL_SCRIPT_MODE:
544                         for (FreestyleModuleConfig *module = (FreestyleModuleConfig *)view_layer->freestyle_config.modules.first;
545                              module;
546                              module = module->next)
547                         {
548                                 if (module->script && module->is_displayed)
549                                         count++;
550                         }
551                         break;
552                 case FREESTYLE_CONTROL_EDITOR_MODE:
553                         for (FreestyleLineSet *lineset = (FreestyleLineSet *)view_layer->freestyle_config.linesets.first;
554                              lineset;
555                              lineset = lineset->next)
556                         {
557                                 if (lineset->flags & FREESTYLE_LINESET_ENABLED)
558                                         count++;
559                         }
560                         break;
561         }
562         return count;
563 }
564
565 int FRS_is_freestyle_enabled(ViewLayer *view_layer)
566 {
567         return ((view_layer->flag & VIEW_LAYER_RENDER) &&
568                 (view_layer->flag & VIEW_LAYER_FREESTYLE) &&
569                 displayed_layer_count(view_layer) > 0);
570 }
571
572 void FRS_init_stroke_renderer(Render *re)
573 {
574         if (G.debug & G_DEBUG_FREESTYLE) {
575                 cout << endl;
576                 cout << "#===============================================================" << endl;
577                 cout << "#  Freestyle" << endl;
578                 cout << "#===============================================================" << endl;
579         }
580
581         init_view(re);
582
583         controller->ResetRenderCount();
584 }
585
586 void FRS_begin_stroke_rendering(Render *re)
587 {
588         init_camera(re);
589 }
590
591 Render *FRS_do_stroke_rendering(Render *re, ViewLayer *view_layer, int render)
592 {
593         Render *freestyle_render = NULL;
594
595         if (!render)
596                 return controller->RenderStrokes(re, false);
597
598         RenderMonitor monitor(re);
599         controller->setRenderMonitor(&monitor);
600         controller->setViewMapCache((view_layer->freestyle_config.flags & FREESTYLE_VIEW_MAP_CACHE) ? true : false);
601
602         if (G.debug & G_DEBUG_FREESTYLE) {
603                 cout << endl;
604                 cout << "----------------------------------------------------------" << endl;
605                 cout << "|  " << (re->scene->id.name + 2) << "|" << view_layer->name << endl;
606                 cout << "----------------------------------------------------------" << endl;
607         }
608
609         // prepare Freestyle:
610         //   - load mesh
611         //   - add style modules
612         //   - set parameters
613         //   - compute view map
614         prepare(re, view_layer);
615
616         if (re->test_break(re->tbh)) {
617                 controller->CloseFile();
618                 if (G.debug & G_DEBUG_FREESTYLE) {
619                         cout << "Break" << endl;
620                 }
621         }
622         else {
623                 // render and composite Freestyle result
624                 if (controller->_ViewMap) {
625                         // render strokes
626                         re->i.infostr = IFACE_("Freestyle: Stroke rendering");
627                         re->stats_draw(re->sdh, &re->i);
628                         re->i.infostr = NULL;
629                         g_freestyle.scene = re->scene;
630                         int strokeCount = controller->DrawStrokes();
631                         if (strokeCount > 0) {
632                                 freestyle_render = controller->RenderStrokes(re, true);
633                         }
634                         controller->CloseFile();
635                         g_freestyle.scene = NULL;
636
637                         // composite result
638                         if (freestyle_render) {
639                                 FRS_composite_result(re, view_layer, freestyle_render);
640                                 RE_FreeRenderResult(freestyle_render->result);
641                                 freestyle_render->result = NULL;
642                         }
643                 }
644         }
645
646         return freestyle_render;
647 }
648
649 void FRS_end_stroke_rendering(Render * /*re*/)
650 {
651         // clear canvas
652         controller->Clear();
653 }
654
655 void FRS_free_view_map_cache(void)
656 {
657         // free cache
658         controller->DeleteViewMap(true);
659 #if 0
660         if (G.debug & G_DEBUG_FREESTYLE) {
661                 printf("View map cache freed\n");
662         }
663 #endif
664 }
665
666 //=======================================================
667 //   Freestyle Panel Configuration
668 //=======================================================
669
670 void FRS_copy_active_lineset(FreestyleConfig *config)
671 {
672         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
673
674         if (lineset) {
675                 lineset_buffer.linestyle = lineset->linestyle;
676                 lineset_buffer.flags = lineset->flags;
677                 lineset_buffer.selection = lineset->selection;
678                 lineset_buffer.qi = lineset->qi;
679                 lineset_buffer.qi_start = lineset->qi_start;
680                 lineset_buffer.qi_end = lineset->qi_end;
681                 lineset_buffer.edge_types = lineset->edge_types;
682                 lineset_buffer.exclude_edge_types = lineset->exclude_edge_types;
683                 lineset_buffer.group = lineset->group;
684                 strcpy(lineset_buffer.name, lineset->name);
685                 lineset_copied = true;
686         }
687 }
688
689 void FRS_paste_active_lineset(FreestyleConfig *config)
690 {
691         if (!lineset_copied)
692                 return;
693
694         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
695
696         if (lineset) {
697                 if (lineset->linestyle)
698                         id_us_min(&lineset->linestyle->id);
699                 lineset->linestyle = lineset_buffer.linestyle;
700                 if (lineset->linestyle)
701                         id_us_plus(&lineset->linestyle->id);
702                 lineset->flags = lineset_buffer.flags;
703                 lineset->selection = lineset_buffer.selection;
704                 lineset->qi = lineset_buffer.qi;
705                 lineset->qi_start = lineset_buffer.qi_start;
706                 lineset->qi_end = lineset_buffer.qi_end;
707                 lineset->edge_types = lineset_buffer.edge_types;
708                 lineset->exclude_edge_types = lineset_buffer.exclude_edge_types;
709                 if (lineset->group) {
710                         id_us_min(&lineset->group->id);
711                         lineset->group = NULL;
712                 }
713                 if (lineset_buffer.group) {
714                         lineset->group = lineset_buffer.group;
715                         id_us_plus(&lineset->group->id);
716                 }
717                 strcpy(lineset->name, lineset_buffer.name);
718                 BKE_freestyle_lineset_unique_name(config, lineset);
719                 lineset->flags |= FREESTYLE_LINESET_CURRENT;
720         }
721 }
722
723 void FRS_delete_active_lineset(FreestyleConfig *config)
724 {
725         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
726
727         if (lineset) {
728                 BKE_freestyle_lineset_delete(config, lineset);
729         }
730 }
731
732 /**
733  * Reinsert the active lineset at an offset \a direction from current position.
734  * \return if position of active lineset has changed.
735  */
736 bool FRS_move_active_lineset(FreestyleConfig *config, int direction)
737 {
738         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
739         return (lineset != NULL) && BLI_listbase_link_move(&config->linesets, lineset, direction);
740 }
741
742 // Testing
743
744 Material *FRS_create_stroke_material(Main *bmain, struct FreestyleLineStyle *linestyle)
745 {
746         bNodeTree *nt = (linestyle->use_nodes) ? linestyle->nodetree : NULL;
747         Material *ma = BlenderStrokeRenderer::GetStrokeShader(bmain, nt, true);
748         ma->id.us = 0;
749         return ma;
750 }
751
752 } // extern "C"